Статьи

Обучающее руководство по образцу декоратора с примерами Java

Сегодняшний шаблон — это шаблон Decorator, который позволяет динамически расширять поведение класса во время выполнения.

Декоратор в реальном мире

Концепция декоратора заключается в том, что он динамически добавляет дополнительные атрибуты к объекту. Реальным примером этого может быть рамка для фотографий. Картинка — это наш объект, который имеет свои особенности. Для демонстрации мы добавляем рамку к картине, чтобы украсить ее. Вы, наверное, уже знакомы с концепцией объектов-оболочек, и, по сути, именно этим и является Декоратор. 

Refcard Designs Patterns
Для лучшего обзора самых популярных шаблонов дизайна, лучше всего начать с Refcard Designs Patterns от DZone .

Образец Декоратора

Декоратор известен как структурный шаблон, так как он используется для формирования больших структур объектов во многих разнородных объектах. Определение Decorator, представленное в оригинальной книге «Банды четырех» на DesignPatterns, гласит:

Позволяет динамически оборачивать объекты, чтобы изменить их существующие обязанности и поведение

Традиционно, вы могли бы рассмотреть подклассы как лучший способ приблизиться к этому — но будут случаи, когда подклассификация невозможна или нецелесообразна. Это приводит нас к принципу Open / Closed : классы должны быть открыты для расширения, но закрыты для модификации. Это хороший принцип, который нужно иметь в виду, поскольку он сохраняет стабильность вашего класса, но оставляет его открытым для расширения, если кто-то хочет добавить поведение. 

Давайте посмотрим на определение диаграммы, прежде чем углубляться в детали.

Компонент определяет интерфейс для объектов , которые могут быть добавлены динамически responsibilties, а ConcreteComponent это просто реализация этого интерфейса. Декоратор имеет ссылку на компонент , а также соответствует интерфейсу компоненты. Это важная вещь, которую нужно помнить, поскольку Декоратор, по сути, оборачивает Компонент. ConcreteDecorator только добавляет обязанности к исходному компоненту. 

Буду ли я использовать этот шаблон?

Шаблон Decorator следует использовать, когда:

  • Обязанности и поведение объекта должны быть динамически модифицируемыми
  • Конкретные реализации должны быть отделены от ответственности и поведения

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

Так как это работает в Java?

Вы увидите декораторы, используемые в потоках ввода / вывода Java. Потоковые классы расширяют базовые подклассы, добавляя функции в потоковые классы.

В нашем примере мы будем использовать электронные письма для иллюстрации декоратора. 

Сначала у нас есть интерфейс электронной почты, у которого есть метод getContents: 

 

public interface IEmail{   public String getContents();   }

И мы предоставим конкретную реализацию для использования: 

//concrete componentpublic class Email implements IEmail{   private String content;      public Email(String content)   {      this.content = content;   }   @Override   public String getContents()   {      //general email stuff      return content;   }}

Теперь мы создадим декоратор, который обернет базовую электронную почту дополнительной функциональностью. Мы смоделируем это как абстрактный класс и сохраним ссылку на базовую электронную почту.

public abstract class EmailDecorator implements IEmail{   //wrapped component   IEmail originalEmail;      }

Допустим, к электронным письмам, которые покидают внутренний сервер компании, в конце должен быть добавлен отказ от ответственности. Мы можем просто добавить декоратор для обработки этого: 

//concrete decoratorpublic class ExternalEmailDecorator extends EmailDecorator{   private String content;       public ExternalEmailDecorator(IEmail basicEmail)   {      originalEmail = basicEmail;   }      @Override   public String getContents()   {            //  secure original       content = addDisclaimer(originalEmail.getContents());      return content;   }         private String addDisclaimer(String message)   {      //append company disclaimer to message      return  message + "\n Company Disclaimer";   }   }

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

//concrete decoratorpublic class SecureEmailDecorator extends EmailDecorator{   private String content;       public SecureEmailDecorator(IEmail basicEmail)   {      originalEmail = basicEmail;   }      @Override   public String getContents()   {            //  secure original       content = encrypt(originalEmail.getContents());      return content;   }         private String encrypt(String message)   {      //encrypt the string       return  encryptedMessage;   }   }

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

 

public class EmailSender{      public void sendEmail(IEmail email)   {      //read the email to-address, to see if it's going outside of the company      //if so decorate it       ExternalEmailDecorator external = new ExternalEmailDecorator(email);      external.getContents();      //send          }}

Остерегайтесь недостатков

Злоупотребление принципом Open / Closed может привести к абстрактному и сложному коду. Этот принцип действительно должен использоваться только там, где код менее всего может измениться.

Книга Design Patterns действительно указывает на пару недостатков этого шаблона. Декораторы могут привести к созданию системы с большим количеством меньших объектов, которые будут похожи на разработчика и создадут головную боль при обслуживании. Кроме того, Decorator и его вложенные компоненты не идентичны, поэтому тесты для типа объекта (instanceof) не пройдут.

Следующий

 Следующим паттерном будет «Цепочка ответственности».

Наслаждайтесь всей серией «Design Patterns Uncovered»:

Образцы творчества

Структурные паттерны

Поведенческие образцы