Я читал статью по программированию, в которой упоминался шаблон Decorator. Я программировал некоторое время, но без какого-либо формального образования или обучения, но я пытаюсь узнать о стандартных шаблонах и тому подобном.
Поэтому я посмотрел Декоратор и нашел статью в Википедии . Теперь я понимаю концепцию шаблона Decorator, но меня немного смутил этот отрывок:
В качестве примера рассмотрим окно в оконной системе. Чтобы разрешить прокрутку содержимого окна, мы можем добавить к нему горизонтальные или вертикальные полосы прокрутки, в зависимости от ситуации. Предположим, что окна представлены экземплярами класса Window, и предположим, что этот класс не имеет функций для добавления полос прокрутки. Мы могли бы создать подкласс ScrollingWindow, который их предоставляет, или мы могли бы создать ScrollingWindowDecorator, который добавляет эту функциональность к существующим объектам Window. На данный момент, любое решение будет в порядке.
Теперь давайте предположим, что мы также хотим добавить возможность добавить границы в наши окна. Опять же, наш оригинальный класс Window не имеет поддержки. Подкласс ScrollingWindow теперь создает проблему, потому что он эффективно создал окно нового типа. Если мы хотим добавить поддержку границ для всех окон, мы должны создать подклассы WindowWithBorder и ScrollingWindowWithBorder. Очевидно, что эта проблема усугубляется с каждой добавляемой новой функцией. Для решения для декоратора мы просто создаем новый BorderedWindowDecorator - во время выполнения мы можем декорировать существующие окна с помощью ScrollingWindowDecorator или BorderedWindowDecorator или обоих, как мы сочтем нужным.
Хорошо, когда говорят добавить границы ко всем окнам, почему бы просто не добавить функциональность в исходный класс Window, чтобы разрешить эту опцию? На мой взгляд, подклассы предназначены только для добавления определенных функций в класс или переопределения метода класса. Если бы мне нужно было добавить функциональность ко всем существующим объектам, почему бы мне просто не изменить суперкласс для этого?
В статье была еще одна строка:
Шаблон декоратора является альтернативой подклассам. Подклассы добавляют поведение во время компиляции, и изменение влияет на все экземпляры исходного класса; Декорирование может обеспечить новое поведение во время выполнения для отдельных объектов.
Я не понимаю, где они говорят «... изменение влияет на все экземпляры исходного класса» - как подклассы изменяют родительский класс? Разве это не весь смысл подкласса?
Я собираюсь предположить, что статья, как и многие вики, просто не написана четко. Я вижу полезность Декоратора в последней строке - «... обеспечить новое поведение во время выполнения для отдельных объектов».
Не читая об этом шаблоне, если бы мне нужно было изменить поведение во время выполнения для отдельных объектов, я бы, вероятно, встроил некоторые методы в супер- или подкласс для включения / отключения указанного поведения. Пожалуйста, помогите мне действительно понять полезность Декоратора, и почему мое мышление новичка неверно?
Ответы:
Шаблон декоратора - тот, который предпочитает композицию перед наследованием [еще одна парадигма ООП, о которой полезно узнать]
Основное преимущество шаблона декоратора - по сравнению с подклассами - это расширение возможностей смешивания и сопоставления. Если у вас есть, например, 10 различных поведений, которые может иметь окно, то это означает - с помощью подклассов - вам нужно создать каждую другую комбинацию, которая также неизбежно будет включать много повторного использования кода.
Однако что произойдет, когда вы решите добавить новое поведение?
С помощью декоратора вы просто добавляете новый класс, который описывает это поведение, и все - шаблон позволяет вам эффективно добавить это без каких-либо изменений в остальной части кода.
С подклассами у вас есть кошмар на руках.
Один вопрос, который вы задали, был: "Как подклассификация меняет родительский класс?" Дело не в том, что это меняет родительский класс; когда он говорит экземпляр, это означает любой объект, который вы «создали» [если вы используете Java или C #, например, с помощью
new
команды]. Это означает, что когда вы добавляете эти изменения в класс, у вас нет выбора для того, чтобы это изменение было, даже если оно вам на самом деле не нужно.В качестве альтернативы, вы можете поместить все его функциональные возможности в один класс, включив / выключив его с помощью флагов ... но это заканчивается тем, что один класс становится все больше и больше по мере роста вашего проекта.
Нет ничего необычного в том, чтобы начать свой проект таким образом и преобразовать его в шаблон декоратора, как только вы достигнете эффективной критической массы.
Интересный момент, на который следует обратить внимание: вы можете добавлять одну и ту же функциональность несколько раз; так что вы можете, например, иметь окно с двойным, тройным или любым другим количеством или границами, как вам нужно.
Основная задача шаблона - включить изменения во время выполнения: вы можете не знать, как вы хотите, чтобы окно выглядело до тех пор, пока программа не запустится, и это позволяет вам легко изменять его. Конечно, это можно сделать с помощью подклассов, но не так хорошо.
И, наконец, он позволяет добавлять функциональные возможности в классы, которые вы, возможно, не сможете редактировать - например, в закрытых / окончательных классах или классах, предоставляемых из других API.
источник
Рассмотрим возможности добавления полос прокрутки и границ с помощью подклассов. Если вы хотите каждую возможность, вы получаете четыре класса (Python):
Теперь
WindowWithScrollBarAndBorder.draw()
окно get рисуется дважды, и во второй раз оно может или не может перезаписать уже нарисованную полосу прокрутки, в зависимости от реализации. Таким образом, ваш производный код тесно связан с реализацией другого класса, и вам нужно заботиться об этом каждый раз, когда вы изменяетеWindow
поведение класса. Решение состоит в том, чтобы скопировать и вставить код из суперклассов в производные классы и настроить его в соответствии с потребностями производных классов, но каждое изменение в суперклассе должно быть снова скопировано и снова откорректировано, поэтому снова производные классы тесно связан с базовым классом (через необходимость копирования-вставки-настройки). Другая проблема заключается в том, что если вам нужно еще одно свойство, которое может иметь или не иметь окно, вам придется удваивать каждый класс:Это означает, что для набора взаимно независимых функций f с | f | число функций, вы должны определить 2 ** | f | классы, например если у вас есть 10 функций, вы получаете 1024 класса с жесткой связью. Если вы используете шаблон Decorator, каждая функция get имеет свой независимый и слабо связанный класс, и у вас есть только 1 + | f | классы (это 11 для примера выше).
источник
Я не эксперт по этому конкретному шаблону, но, как я вижу, шаблон Decorator можно применять к классам, которые вы не можете изменять или подклассить (например, они могут не быть вашим кодом и быть запечатаны). ). В вашем примере, что если вы не написали класс Window, а просто его потребляете? Пока класс Window имеет интерфейс, и вы программируете на этом интерфейсе, ваш Декоратор может использовать тот же интерфейс, но расширять функциональность.
Пример, который вы упомянули, на самом деле описан здесь довольно аккуратно:
http://www.oodesign.com/decorator-pattern.html
источник