Я могу решить проблему добавления функциональности, добавив подклассы, тогда зачем мне использовать шаблон декоратора, в чем реальное преимущество шаблона декоратора?
Шаблон декоратора против подкласса
Ответы (6)
из шаблона декоратора в Википедии
Шаблон декоратора можно использовать для расширения (украшения) функциональности определенного объекта во время времени выполнения.
Весь смысл шаблона декоратора заключается в динамическом добавлении дополнительного поведения/функциональности, что, конечно же, невозможно во время разработки.
из той же статьи:
Шаблон декоратора является альтернативой созданию подклассов. Подклассы добавляют поведение во время компиляции, и это изменение влияет на все экземпляры исходного класса; декорирование может обеспечить новое поведение отдельных объектов во время выполнения.
Пример из GoF:
Предположим, у вас есть класс TextView. Затем в каком-то месте вам нужно прокручиваемое текстовое представление, поэтому вы создаете подкласс TextView и создаете класс ScrolledTextView. А в другом месте вам нужна рамка вокруг текстового представления. Итак, вы снова создаете подкласс и создаете BorderedTextView. Ну, теперь в каком-то месте вы хотите границы и прокрутки обоих. Ни один из двух предыдущих подклассов не обладает обеими возможностями. Поэтому вам нужно создать 3-й. При создании ScrolledBorderedTextView вы фактически дублируете усилия. Вам не нужен этот класс, если у вас есть способ скомпоновать возможности двух предыдущих. Что ж, дела могут пойти еще хуже, и это может привести к ненужному взрыву класса.
По сути, с помощью шаблона декоратора вы можете добавить любое количество дополнительных обязанностей к объекту во время выполнения, чего вы не можете достичь путем создания подклассов без потенциального повреждения структуры вашего кода или добавления множества повторяющихся комбинаций для подклассов.
Но во-первых, Шаблоны проектирования — это не то, что вы должны использовать.
Нужен ли шаблон или нет, зависит от вашей конкретной проблемы, вы хотите поддерживать кода в течение длительного времени или нет, независимо от того, хотите ли вы его расширять или нет, а также от многих других подобных факторов.
И не существует шаблона, который был бы полезен во всех случаях.
Шаблон (декоратор или что-то еще), подходящее для одной ситуации, может не подойти для другой ситуации.
В книге GoF Design Patterns указаны два основных преимущества использования Декораторы над подклассами:
Больше гибкости, чем статическое наследование. Шаблон декоратора обеспечивает более гибкий способ добавления обязанностей к объектам, чем при статическом (множественном) наследовании. С помощью декораторов обязанности можно добавлять и удалять во время выполнения, просто присоединяя и отсоединяя их. Напротив, наследование требует создания нового класса для каждой дополнительной ответственности (например, BorderedScrollableTextView, BorderedTextView). Это порождает множество классов и увеличивает сложность системы. Кроме того, предоставление разных классов Decorator для определенного класса Component позволяет вам смешивать и сопоставлять обязанности.
Декораторы также упрощают добавление свойства дважды. Например, чтобы дать TextView двойную границу, просто прикрепите два BorderDecorator. Наследование от класса Border дважды в лучшем случае подвержено ошибкам.
Избегает нагруженных функциями классов, стоящих выше в иерархии. Decorator предлагает подход с оплатой по мере использования для добавления обязанностей. Вместо того, чтобы пытаться поддерживать все предсказуемые функции в сложном, настраиваемом классе, вы можете определить простой класс и постепенно добавлять функциональные возможности с помощью объектов Decorator. Функциональность может быть составлена из простых частей. В результате приложению не нужно платить за функции, которые оно не использует. Также легко определить новые виды декораторов независимо от классов объектов, которые они расширяют, даже для непредвиденных расширений. Расширение сложного класса приводит к раскрытию деталей, не связанных с добавляемыми обязанностями.
С моей точки зрения, предотвращение взрыва подкласса само по себе весьма убедительно.
Если у вас есть TextWindow
, к которому вы хотите добавить горизонтальную прокрутку, вертикальную прокрутку и границы независимо и необязательно, используя подклассы, вам нужно определить подклассы для HorizontalScrollingTextWindow
, VerticalScrollingTextWindow
, HorizontalAndVerticalScrollingTextWindow
, BorderedTextWindow
, HorizontalScrollingBorderedTextWindow
, VerticalScrollingBorderedTextWindow
, HorizontalAndVerticaScrollingBorderedTextWindow
и больше, если вы заботитесь о порядке прокрутки и границ.
С декораторами вам нужно определить только два декоратора прокрутки и один декоратор границы.
Создание подклассов может привести к проблемам с принципом замещения Лисков. Декоратор избегает этого.
Еще одним преимуществом декоратора является то, что вы пишете (принуждаете писать) к интерфейсу. Это упрощает тестирование. Это правда, что ваша иерархия объектов также может быть записана в интерфейс и, таким образом, иметь некоторые из тех же преимуществ, однако я могу протестировать одну реализацию класса декоратора изолированно. Я не могу сделать то же самое с подклассом, потому что я всегда буду возвращать всю иерархию к базовому классу. Я не могу тестировать новый код изолированно.
Используя шаблон декоратора и следуя принципу единой ответственности, я могу создать несколько декораторов и расположить их так, как захочу. Я могу настроить это во время выполнения. При наследовании я либо должен создать все возможные ветви (a->b->c, затем a->c->b, тем самым дублируя код и увеличивая количество тестов), либо я создаю 1 иерархию, а затем добавляю другую, когда это необходимо, но это запускает новый цикл тестирования/релиза.
Вот почему вы хотели бы использовать шаблон декоратора вместо подкласса.
Вот различия, основанные на реальной реализации.
Оформление — это альтернативный способ создания подклассов для расширения функциональности существующих классов. Вот некоторый сценарий, в котором мы должны использовать подкласс или декоратор.
1) Подклассы в основном используются в случае расширения функциональности аналогичной группы классов, которая хочет сохранить старую функциональность, а также новую в подклассах, и все экземпляры подклассов имеют одну и ту же функциональность. Если мы внесем изменения в дочерние классы, то это отразит все экземпляры дочерних классов. Например, отношение иерархии, аналогичная группа классов.
Родитель->Ребенок->Внук.
Автомобиль->Maruti 800->Maruti 100 (будет иметь функцию Maruti 800, а также новый)
2) Шаблон декоратора используется для украшения существующих классов без изменения старого поведения. Например, круг класса имеет простую границу, но нам нужно украсить его красной рамкой, через некоторое время какой-то пользователь хочет круг с желтым цветом, а кто-то хочет круг с зеленой рамкой, и некоторые пользователи хотят круг с красной и желтой рамкой, а некоторые пользователи хотят круг с красной и зеленой рамкой и т. д., для этого это идеальный шаблон, поскольку он не уменьшает количество комбинаций классов. Ниже приведен пример.
Icircle cir=новый RedDecorator(новый круг()), украшающий круг красным цветом
Icircle cir=new YellowDecorator(new Circle()), украшающий круг желтым цветом
Icircle cir=новый RedDecorator(новый YellowDecorator(новый круг())) украшающий
круг с красным и желтым, здесь нам не нужно создавать декоратор класса RedAndYellow. таким же образом мы можем украсить круг другим набором комбинаций, не создавая новый набор классов комбинаций.
Таким образом, это уменьшает количество комбинированных классов.
Вот полезная ссылка на шаблон декоратора
https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm
Гибкость, вот причина IMO.
TextView
). - person Dherik   schedule 14.02.2017