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).