Шаблон проектирования декоратора помог мне избежать написания сложного кода структуры наследования. Код, который не только сложно написать, но еще труднее понять и поддерживать, а также лучше структурировать код. В этой статье подробно объясняется шаблон проектирования декоратора.

Сначала давайте посмотрим, что говорит GOF:

Динамически прикрепляйте дополнительные обязанности к объекту. Декораторы предоставляют гибкую альтернативу подклассам для расширения функциональности.

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

Ниже приведена UML-диаграмма шаблона проектирования декоратора.

Давайте попробуем понять структуру UML.

Во-первых, я хочу отметить, что у нас есть два абстрактных класса Component и Decorator и два конкретных класса ConcreteComponent и ConcreteDecorator [мы можем создать дополнительную конкретную реализацию класса декоратора в соответствии с нашим требованием.]. ConcreteComponent является реализацией абстрактного класса Component, тогда как ConcreteDecorator представляет собой Decorator.

Однако класс Decorator является Специальным классом, из диаграммы видно, что класс Decorator реализует класс Component, а также агрегирует Тип компонента.

Таким образом, мы можем сказать, что класс Decorator имеет связь как "IS-A", так и "HAS-A" с Компонент Класс. И именно этот дизайнерский трюк делает шаблон проектирования декоратора таким гибким.

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

Итак, вот сценарий требования:

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

Вы можете попробовать разработать решение самостоятельно, прежде чем читать мое решение.

Ниже приведен пример кода для шаблона проектирования декоратора:

Во-первых, давайте напишем абстрактный класс Coffee, соответствующий классу Component на диаграмме UML.

Класс Coffee имеет два абстрактных метода: GetCost(), который возвращает цену, и GetIngredient(), который возвращает название ингредиента.

Следующий класс SimpleCoffee является конкретной реализацией класса Coffee и имеет реализацию GetCost() и GetIngredient() абстрактные методы, которые возвращают цену кофе и имя соответственно.

Класс SimpleCoffee соответствует ConcreteComponent на диаграмме UML.

Теперь о классе Decorator. Помните, что декоратор реализует компонент и принимает тип компонента в качестве агрегата. Поэтому я создаю класс CoffeeDecorator, который реализует абстрактный класс Coffee и принимает тип Coffee, то есть SimpleCoffee. через конструктор.

CoffeeDecorator просто привязывается к агрегированному объекту Coffee.

Давайте теперь реализуем первый класс ингредиентов, который является конкретной реализацией CoffeeDecorator.

Итак, наш первый класс ингредиентов WithMilk добавляет молоко к кофе. Добавление молока стоит 0,5 доллара. поэтому метод GetCost() добавляет 0,5 к базовому методу GetCost(). И аналогичным образом добавляет строку «С молоком» в базовый метод GetIngredient().

Вот код нашего клиента. Я добавил статический метод DisplayInfo(), чтобы упростить отображение информации о стоимости и заказе.

ВЫВОД :
Стоимость: $1
Клиент заказал кофе
============================ ========
Стоимость: 1,5 доллара США
Клиент заказал кофе с молоком
===================== ================

Теперь наш клиент сообщает нам, что в меню добавлен новый ингредиент Ванильный сироп, и покупатели могут добавлять его в кофе за дополнительную плату в размере 1 доллара США.

Чтобы включить VanillaSyrup, я добавляю еще один класс, реализующий Decorator. Поэтому я создаю новый класс WithVanillaSyrup и исправляю метод GetCost(), добавляя $1, а метод GetIngredient() возвращает строку «с ванильный сироп».

Ниже приведен код класса WithVanillaSyrup:

Теперь наш клиентский код будет выглядеть следующим образом:

ВЫВОД:
Стоимость: $2
Клиент заказал Кофе с ванильным сиропом
========================== ===========
Стоимость: $2.5
Клиент заказал Кофе с ванилью, Сироп с молоком
=============== ======================

Подводя итоги, шаблон проектирования Decorator позволяет нам добавлять или расширять поведение объекта во время выполнения. Это помогает нам избегать кода, который реализует несколько подуровней и сложное наследование, делая наш код несвязанным, простым в обслуживании и понятным. Шаблон проектирования декоратора также придерживается принципа открытого и закрытого дизайна.

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