Другое

Декоратор: что это и что он делает, виды Декораторов

Lorem ipsum dolor

Декоратор — это специалист по художественному оформлению интерьера и экстерьера. Он делает все красивым: квартиру, торговый центр, личный дворик, большие ландшафты и др. При чем здесь Декоратор и программирование? спросите вы.

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

Паттерн «Декоратор»

Паттерн «Декоратор» применяется в том случае, когда объекту необходимо придать какие-то дополнительные свойства в динамическом режиме. Но когда потребность в этих свойствах исчезнет, они должны быть сняты с объекта. Также он применяется в том случае, когда невозможно применить наследование. Например:

  • когда объекту нужно придать множество разных свойств, а для каждого свойства нужно определить собственный класс, таким образом, количество классов очень сильно «раздуется»;

  • когда с объекта нужно «снять» весь основной функционал и временно придать другой, но с сохранением основного;

  • когда нужно наследовать поведение от множества классов и придать их одному объекту;

  • и др.

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

И новый, и старый объект имеют один интерфейс. Поэтому для конечного пользователя «обертывание» оставляет тот же принцип работы с объектом. Меняются только свойства, заложенные в «обертке».

Работу паттерна «Декоратор» можно проследить в жизни. Человек — это обычный объект. Одежда на человеке — это паттерн «Декоратор» или «обертка». Неважно, как одет человек, — он всегда остается одним и тем же человеком с основным набором свойств. Однако, когда человек выходит зимой на улицу — важно одеться, чтобы не замерзнуть. Если человек занимается спортом — важно, чтобы одежда соответствовала. Если у человека на работе дресс-код «строгий стиль», значит, он должен надеть «строгую» одежду, а не прийти в спортивном костюме. То есть разная одежда — это свойства разных классов: «зима», «спорт», «работа» и др.

Как реализовать паттерн «Декоратор» 

Как реализовать паттерн «Декоратор» при помощи кода — мы покажем чуть ниже. Для реализации паттерна «Декоратор» нужно проделать определенную работу. Например:

  1. Удостоверьтесь, что у вас есть основной объект для «обертывания» и дополнительные свойства, которые нужно ему придать.

  2. Создайте единый интерфейс объекта без дополнительных свойств и с дополнительными свойствами.

  3. Разработайте класс основного объекта, куда будет помещаться его основная бизнес-логика.

  4. Разработайте основной класс паттерна «Декоратор». Такой класс имеет поля для хранения ссылок на дополнительные свойства других классов.

  5. Основной объект и класс «Декоратор» должны управляться одним интерфейсом.

Паттерн «Декоратор»: преимущества, недостатки и сравнение с другими паттернами 

Среди преимуществ паттерна «Декоратор» можно отметить:

  • высокую гибкость, по сравнению с наследованием;

  • динамическое придание свойств объекту;

  • множественное придание свойств одному объекту;

  • и др.

Среди недостатков можно отметить:

  • усложненную конфигурацию объектов при многократном «обертывании» одного и того же объекта;

  • множество небольших классов.

Паттерн «Декоратор», по сравнению с другими паттернами:

  1. «Декоратор» улучшает основной объект дополнительными свойствами, не изменяя интерфейс. А паттерн «Адаптер» делает то же самое, но изменяет интерфейс основного объекта.

  2. «Декоратор» улучшает прежний интерфейс. Паттерн «Адаптер» приносит альтернативный интерфейс. Паттерн «Заместитель» не трогает интерфейс вообще.

  3. Паттерны «Декоратор» и «Цепочки обязанностей» очень похожи. Однако «Декоратор» не влияет на работу других «декораторов», а «Цепочка обязанностей» в любой момент может прервать свои «рабочие звенья».

  4. Паттерны «Декоратор» и «Компоновщик» также похожи. Однако «Декоратор» воздействует только на один объект, а «Компоновщик» может сразу на несколько.

  5. Паттерн «Декоратор» воздействует на объект «снаружи», а паттерн «Стратегия» воздействует на объект «изнутри».

Паттерн «Декоратор»: примеры реализации

Паттерн «Декоратор» применяется во многих языках программирования. Например:

  • Kotlin;
  • Ruby;
  • Java;
  • C#;
  • C++;
  • PHP;
  • JavaScript;
  • CoffeeScript;
  • Swift;
  • и др.

Приведем пример шаблона паттерна «Декоратор» на нескольких языках программирования.

Паттерн «Декоратор» на С++:

 class Object {

 

 public:

  virtual ~Object() {}

  virtual std::string Operation() const = 0;

};

 

class ConcreteObject : public Object {

 public:

  std::string Operation() const override {

    return "ConcreteObject";

  }

};

 

class Decoration : public Object {

  

 protected:

  Object* component_;

 

 public:

  Decoration(Object* component) : component_(component) {

  }

  

  std::string Operation() const override {

    return this->component_->Operation();

  }

};

 

class ConcreteDecorationA : public Decoration {

 

 public:

  ConcreteDecorationA(Object* component) : Decoration(component) {

  }

  std::string Operation() const override {

    return "ConcreteDecorationA(" + Decoration::Operation() + ")";

  }

};

 

class ConcreteDecorationB : public Decoration {

 public:

  ConcreteDecorationB(Object* component) : Decoration(component) {

  }

 

  std::string Operation() const override {

    return "ConcreteDecorationB(" + Decoration::Operation() + ")";

  }

};

 

void ClientCode(Object* component) {

  // ...

  std::cout << "RESULT: " << component->Operation();

  // ...

}

 

int main() {

 

  Object* simple = new ConcreteObject;

  std::cout << "Client: Я назначаю простой объект :\n";

  ClientCode(simple);

  std::cout << "\n\n";

 

  Object* decorator1 = new ConcreteDecorationA(simple);

  Object* decorator2 = new ConcreteDecorationB(decoration1);

  std::cout << "Client: Сейчас я назначаю декорированный объект:\n";

  ClientCode(decoration2);

  std::cout << "\n";

 

  delete simple;

  delete decoration1;

  delete decoration2;

 

  return 0;

}

 

Код на Ruby:

module DecorationPattern

 

  class Object

    def initialize(line)

      @line = line

    end

 

    def write_line

      @line

    end

  end



  module Decoration

    def initialize(object)

      @object = object

    end

 

    def write_line

      raise NotImplementedError

    end

  end



  class Upcaser

    include Decoration

 

    def write_line

      @object.write_line.upcase

    end

  end

 

  class Timestamper

    include Decoration

 

    def write_line

      "#{Time.now.strftime('%H:%m')} #{@object.write_line}"

    end

  end



  class Datestamper

    include Decoration

 

    def write_line

      "#{Time.now.strftime('%d.%m.%y')} #{@object.write_line}"

    end

  end

 

  def self.run

    puts '=>  Decoration'

 

    object = Object.new('Написан какой-то текст')

    puts "Object:\n=>  #{object.write_line}"

 

    upcased = Upcaser.new(object)

    puts "Upcased:\n=>  #{upcased.write_line}"

 

    timestamped = Timestamper.new(object)

    puts "Timestamped:\n=>  #{timestamped.write_line}"

 

    datestamped = Datestamper.new(object)

    puts "Datestamped:\n=>  #{datestamped.write_line}"

 

    upcased_timestamped = Timestamper.new(Upcaser.new(object))

    puts "Upcased and timestamped:\n=>  #{upcased_timestamped.write_line}"

 

    upcased_datestamped_timestamped = Datestamper.new(Timestamper.new(Upcaser.new(object)))

    puts "Upcased, datestamped and timestamped:\n=>  #{upcased_datestamped_timestamped.write_line}"

 

    datestamped_timestamped = Datestamper.new(Timestamper.new(object))

    puts "Datestamped and timestamped:\n=>  #{datestamped_timestamped.write_line}"

 

    puts ''

  end

end

 

DecorationPattern.run 

 

Код на PHP:

<?php

 

 

interface MyText

{

    public function show();

}

 

class TextPresentation implements  MyText

{

    protected $object;

 

    public function __construct(MyText $text) {

        $this->object = $text;

    }

 

    public function show() {

        echo 'Привет';

        $this->object->show();

    }

}

 

class MyWorld implements  MyText

{

    protected $object;

 

    public function __construct(MyText $text) {

        $this->object = $text;

    }

 

    public function show() {

        echo 'друзья';

        $this->object->show();

    }

}

 

class MySpace implements  MyText

{

    protected $object;

 

    public function __construct(MyText $text) {

        $this->object = $text;

    }

 

    public function show() {

        echo ' ';

        $this->object->show();

    }

}

 

class MyEmpty implements MyText

{

    public function show() {

    }

}

 

$decorator = new TextPresentation(new MySpace(new MyWorld(new MyEmpty())));

$decorator->show(); // Привет друзья

echo '<br />' . PHP_EOL;

$decorator = new MyWorld(new MySpace(new TextPresentation(new MyEmpty())));

$decorator->show(); // друзья Привет

Заключение

Паттерн «Декоратор» удобный инструмент, однако им нужно научиться пользоваться. Его удобно использовать в программах, когда объект один, а «обертка» будет разная. При этом от «обертки» зависит конечный продукт для клиента. В жизни это может быть:

  • заказ пиццы онлайн, когда от выбора начинки и количества ингредиентов зависит конечная стоимость пиццы для клиента;

  • выбор кофе онлайн, когда от сорта и объема кофе также будет зависеть его конечная стоимость;

  • выбор одежды, когда от материала изготовления, цвета и размера зависит стоимость продукта для клиента;

  • и др.

Схожие статьи

Стартапы в кризис: как открыть свое дело и не остаться банкротом?
Другое

Стартапы в кризис: как открыть свое дело и не остаться банкротом?

Как победить прокрастинацию и начать действовать, а не отдыхать
Другое

Как победить прокрастинацию и начать действовать, а не отдыхать

Жанр игр «рогалик» — что это такое?
Другое

Жанр игр «рогалик» — что это такое?

Системный аналитик DWH и его отличия от других подобных профессий
Другое

Системный аналитик DWH и его отличия от других подобных профессий

×