SOLID Принципы: интерфейсы против абстрактных классов

У меня есть конкретный крайний случай, когда я не уверен, использовать ли интерфейс или абстрактный класс. Первоначально мой дизайн использовал абстрактный класс, где методы были реализованы как protected internal abstract void,

Это было полезно, потому что я хотел, чтобы методы вызывались из моей сборки, но были доступны только через наследование вне моей сборки.

Проблема, с которой я столкнулся, заключалась в том, что не все производные абстрактного класса были необходимы для реализации функциональности, поэтому по сути я получил пустые тела методов.

Именно здесь у меня возникла идея использовать интерфейсы, что означало, что я мог реализовать необходимую функциональность через интерфейс, однако это означало, что методы должны быть общедоступными, и, как указано выше, я хочу, чтобы методы были доступны только через наследование.,

Пример использования абстрактного класса:

abstract class Dependency
{
    protected internal abstract void DoThis();
    protected internal abstract void DoThat();
}

sealed class DerivedDependency : Dependency
{
    protected override void DoThis()
    {
        // implementation...
    }

    protected override void DoThat()
    {
        // not required...
    }
}

Пример использования интерфейсов:

abstract class Dependency
{
    // whilst this is now empty, it is still required to provide a type hierarchy!
}

interface IDoThis
{
    void DoThis();
}

interface IDoThat
{
    void DoThat();
}

sealed class DerivedDependency : Dependency, IDoThis
{
    public virtual void DoThis()
    {
        // I only need this functionality. I don't need to DoThat();
    }
}

Поэтому мне интересно, что лучше на самом деле. Оба предлагают функции, которые я хочу, но кажется, что я не могу иметь оба.

Должен ли я просто мириться с наличием пустых тел методов, которые могут реализовывать третьи стороны, не понимая, что они не предназначены для реализации, или же я реализую только необходимые функции, но рискую подвергнуть их функциональности через реализацию?

2 ответа

Решение

Упоминание, что вам не хватает, - это принцип разделения интерфейсов, вы должны использовать два интерфейса: один для внутренних потребителей (методы, помеченные как внутренние), а другой для внешних.

Так что я бы не стал использовать ни абстрактный класс, ни интерфейс, а два (или более) интерфейса.

Возможно, я нашел решение, которое, кажется, работает хорошо, но мне хотелось бы узнать мнение сообщества о том, соблюдает ли это принципы SOLID или нарушает их:

interface IDoThis
{
    void DoThis();
}

class Dependency : IDoThis
{
    protected internal virtual void DoThis()
    {
        // impl.
    }

    void IDoThis.DoThis()
    {
        this.DoThis();
    }
}

IDoThis idt = new Dependency();
idt.DoThis() // calls into the protected method.

Это позволяет мне разделять функциональные возможности через интерфейсы (ISP), получать к ним общий доступ в сборке и переопределять ее во внешних сборках (OCP).

Другие вопросы по тегам