Decorator vs Strategy, Composite, Presenter

Очень понравилась статья Dan Croak из ThoughtBot, про сравнение паттернов, которые часто путают( Декоратор, Презентер, Стратегия, Композиция). По сути этот пост это перевод статьи для себя.

Decorator

Следуя определению GoF, суть декоратора заключается в следующем: Динамически расширить возможности декорируемого объекта.

Пример:

coffee = Coffee.new
Sugar.new(Milk.new(coffee)).cost

или

coffee = Coffee.new
coffee.extend Milk
coffee.extend Sugar
coffee.cost

Вопрос в чем же отличие декоратора от остальных вышеперечисленных паттернов?

Strategy

GoF:

  • Декоратор меняет наружность
  • Стратегия меняет внутренности

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

Пример:

class Coffee
  def initialize(brewing_strategy = DripBrewingStrategy.new)
    @brewing_strategy = brewing_strategy
  end
  
  def brew
    @brewing_strategy.brew
  end
end

Coffee.new(SteepBrewingStrategy.new)

Основное отличие, что объект пробрасываемый в конструктор не "декорируется", а подменяется, т.е. интерфейс остается тем же, без расширения, но реализация при этом может кардинально отличаться.

Composite

  • Декоратор это композиция с одним объектом
  • Декоратор не предназначен для аггрегации объектов

Пример из ActivePresenter:

class SignupPresenter < ActivePresenter::Base
  presents :user, :account
end

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

Presenter

Самый непонятный из паттернов:

  • Декоратор это класс, добавляющий функциональность другому классу
  • Презентер это класс добавляющий функциональность, отвечающую за представление, другому классу
  • Презентер иногда декоратор
  • Презентер иногда композиция

Определения паттерна Презентер нет в книги GoF. Истоки использования данного термина в rails сообществе идут от статьи Jay Fields 2007 года. По большому счету, единственное что делает презентер презентером, так это его "представительная" составляющая.

class HumanizedStat
  def initialize(component)
    @component = component
  end
  
  def to_s
    # большое выражение, которое обычно хранят в модели
  end
  # хелпер методы необходимы для #to_s
  # которым возможно следовало бы находиться в app/helpers
end

В этом примере Презентер очень похож на Декоратор, но тем не менее не попадает под определение Gang of Four.

Однако функциональность полностью относится к представлению объекта, то можно считать данную конструкцию презентером и сложить её в app/presenters.