Decorator allows you to alter the behavior of a specific object instance. Behavior can be added, changed, or removed.
- Doesn't require inheritance (a bonus when working with ActiveRecord).
- Doesn't require the decorated object to know anything about the decorator.
- Can easily become tightly coupled to the decorated object.
- Can become a soup of tiny decorators.
- When a model needs access to the functionality of another class, but only some of the time.
- When you don't want to permanently alter the behavior of the decorated object.
- When you have multiple behaviors that need to be added to a model.
- When you want to change a model's core behavior from the outside-in (ie augmentation, or decoration).
# app/exhibits/exhibit.rb
require "delegator"
class Exhibit < SimpleDelegator
def initialize(model, context)
super(model)
@model = model
@context = context
# initialize any needed state
end
def self.factory(model, context)
class_name = model.class.name + "Exhibit"
class_name.constantize.new(model, context)
end
end
# app/exhibits/referral_exhibit.rb
class ReferralExhibit < Exhibit
def url
@context.referral_url(@model)
end
end
-# app/views/referrals/shows.html.haml
- @referral = Exhibit.factory(@referral, self)
%p
Referral URL: #{@referral.url}
Strategy allows you to switch and delegate the execution of an algorithm.
- Doesn't clutter your object with algorithm details (ie less-coupled).
- The concrete strategy can be selected or even changed at run-time.
- Doesn't require inheritance (a bonus when working with ActiveRecord).
- The class that uses a strategy has to know something about the strategy.
- Strategies require a context, which can be wasteful.
- When a executing a non-trivial algorithm for which there are multiple alternatives.
- When you see large if-then-else or case statements, that's a code smell for Strategy.
- When you want to change a model's core behavior from the inside-out.
# app/models/delivery_strategy.rb
class DeliveryStrategy
def deliver(context)
raise NotImplementedError
end
end
# app/models/email_delivery_strategy.rb
class EmailDeliveryStrategy < DeliveryStrategy
def deliver(context)
mail = Mail.new
# …
end
end
# app/models/http_delivery_stragegy.rb
class EmailDeliveryStrategy < DeliveryStrategy
def deliver(context)
Net::HTTP.new do
# …
end
end
end
# app/models/lead.rb
class Lead < ActiveRecord::Base
attr\_accessor :delivery\_strategy
def deliver
delivery_strategy.deliver(self)
end
end
- Objects on Rails
- Design Patterns (aka Gang of Four) (Also available in the LMP library.)
- Decorator at the Portland Pattern Repository
- Strategy at the Portland Pattern Repository