🎉 Ура! После трёх лет работы, я наконец выпустил английскую версию книги о паттернах! Вот она »
Декоратор

Декоратор на Ruby

Декоратор — это структурный паттерн, который позволяет добавлять объектам новые поведения на лету, помещая их в объекты-обёртки.

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

Особенности паттерна на Ruby

Сложность:

Популярность:

Применимость: Паттерн можно часто встретить в Ruby-коде, особенно в коде, работающем с потоками данных.

Признаки применения паттерна: Декоратор можно распознать по создающим методам, которые принимают в параметрах объекты того же абстрактного типа или интерфейса, что и текущий класс.

Концептуальный пример

Этот пример показывает структуру паттерна Декоратор, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.

main.rb: Пример структуры паттерна

# Базовый интерфейс Компонента определяет поведение, которое изменяется
# декораторами.
class Component
  # @return [String]
  def operation
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Конкретные Компоненты предоставляют реализации поведения по умолчанию. Может
# быть несколько вариаций этих классов.
class ConcreteComponent < Component
  # @return [String]
  def operation
    'ConcreteComponent'
  end
end

# Базовый класс Декоратора следует тому же интерфейсу, что и другие компоненты.
# Основная цель этого класса - определить интерфейс обёртки для всех конкретных
# декораторов. Реализация кода обёртки по умолчанию может включать в себя поле
# для хранения завёрнутого компонента и средства его инициализации.
class Decorator < Component
  attr_accessor :component

  # @param [Component] component
  def initialize(component)
    @component = component
  end

  # Декоратор делегирует всю работу обёрнутому компоненту.
  #
  # @return [String]
  def operation
    @component.operation
  end
end

# Конкретные Декораторы вызывают обёрнутый объект и изменяют его результат
# некоторым образом.
class ConcreteDecoratorA < Decorator
  # Декораторы могут вызывать родительскую реализацию операции, вместо того,
  # чтобы вызвать обёрнутый объект напрямую. Такой подход упрощает расширение
  # классов декораторов.
  #
  # @return [String]
  def operation
    "ConcreteDecoratorA(#{@component.operation})"
  end
end

# Декораторы могут выполнять своё поведение до или после вызова обёрнутого
# объекта.
class ConcreteDecoratorB < Decorator
  # @return [String]
  def operation
    "ConcreteDecoratorB(#{@component.operation})"
  end
end

# Клиентский код работает со всеми объектами, используя интерфейс Компонента.
# Таким образом, он остаётся независимым от конкретных классов компонентов, с
# которыми работает.
#
# @param [Component] component
def client_code(component)
  # ...

  print "RESULT: #{component.operation}"

  # ...
end

# Таким образом, клиентский код может поддерживать как простые компоненты...
simple = ConcreteComponent.new
puts 'Client: I\'ve got a simple component:'
client_code(simple)
puts "\n\n"

# ...так и декорированные.
#
# Обратите внимание, что декораторы могут обёртывать не только простые
# компоненты, но и другие декораторы.
decorator1 = ConcreteDecoratorA.new(simple)
decorator2 = ConcreteDecoratorB.new(decorator1)
puts 'Client: Now I\'ve got a decorated component:'
client_code(decorator2)

output.txt: Результат выполнения

Client: I've got a simple component:
RESULT: ConcreteComponent

Client: Now I've got a decorated component:
RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))

Декоратор на других языках программирования

Декоратор на Java Декоратор на C# Декоратор на PHP Декоратор на Python Декоратор на Swift Декоратор на TypeScript