Наследование паттерна декоратора
В шаблоне декоратора абстрактный класс реализует интерфейс, а конкретный класс (декоратор) расширяет абстрактный класс. Что произойдет с функциональностью шаблона, если конкретный класс напрямую реализует интерфейс вместо того, чтобы наследовать его через абстрактный класс?
2 ответа
Абстрактный класс не требуется. Со страницы 179 книги GoF,
Нет необходимости определять абстрактный класс Decorator, когда вам нужно добавить только одну ответственность. Это часто случается, когда вы имеете дело с существующей иерархией классов, а не разрабатываете новую. В этом случае вы можете объединить ответственность Decorator за пересылку запросов к компоненту в ConcreteDecorator.
Аналогичный вопрос от Head First Design: какова причина перемещения переменной экземпляра в абстрактный класс в шаблоне декоратора?
Оригинальная книга шаблонов проектирования (GoF) датируется 1994 годом и в качестве примеров использует C++ и SmallTalk. IIRC, C++ не имеет интерфейсов на уровне языка (по крайней мере, в 1994 году). Когда книга увещевает
Программа для интерфейса, а не реализация
вы должны интерпретировать слово interface как концепцию, а не языковую конструкцию.
В C++, например, обычно эмулируют интерфейсы с абстрактными классами, которые определяют исключительно чистые виртуальные функции. Многие примеры C++ в книге GoF делают это.
существует Между абстрактными классами и интерфейсамисильная связь , до такой степени, что они фактически взаимозаменяемы.
Что касается паттерна Decorator, я предполагаю, что всегда можно украсить интерфейс. Рассмотрим, например, такой интерфейс:
public interface IFace
{
void Command(Arg1 arg1, Arg2 arg2);
Ret1 Query(Arg3);
}
AFAICT, вы всегда можете написать декоратор
public class MyFace : IFace
{
private readonly IFace inner;
public MyFace(IFace inner)
{
this.inner = inner;
}
public void Command(Arg1 arg1, Arg2 arg2)
{
// Consider doing something interesting here...
inner.Command(arg1, arg2);
// ... or here
}
public Ret1 Query(Arg3)
{
// Consider doing something interesting here...
Ret1 ret = inner.Query(arg3);
// ... or here
return ret;
}
}
Таким образом, для шаблона Decorator не имеет значения, используете ли вы абстрактный класс или интерфейс.