Skip to content

Instantly share code, notes, and snippets.

@ewollesen
Created March 20, 2013 22:35
Show Gist options
  • Save ewollesen/5209176 to your computer and use it in GitHub Desktop.
Save ewollesen/5209176 to your computer and use it in GitHub Desktop.
Talking points for an informal talk given at work.

Decorator

What is it?

Decorator allows you to alter the behavior of a specific object instance. Behavior can be added, changed, or removed.

What's good about it?

  • Doesn't require inheritance (a bonus when working with ActiveRecord).
  • Doesn't require the decorated object to know anything about the decorator.

What's bad about it?

  • Can easily become tightly coupled to the decorated object.
  • Can become a soup of tiny decorators.

When to use it?

  • 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).

Real World Example – Exhibit Pattern (See Objects on Rails)

# 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

What is it?

Strategy allows you to switch and delegate the execution of an algorithm.

What's good about it?

  • 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).

What's bad about it?

  • The class that uses a strategy has to know something about the strategy.
  • Strategies require a context, which can be wasteful.

When to use it?

  • 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.

Real World Example – Lead Delivery

# 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

References

  1. Objects on Rails
  2. Design Patterns (aka Gang of Four) (Also available in the LMP library.)
  3. Decorator at the Portland Pattern Repository
  4. Strategy at the Portland Pattern Repository
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment