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.