Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Decorator Pattern in Ruby

Kerry Buckley
September 22, 2014

The Decorator Pattern in Ruby

Kerry Buckley

September 22, 2014
Tweet

More Decks by Kerry Buckley

Other Decks in Technology

Transcript

  1. “Each pattern describes a problem that occurs over and over

    again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.”
  2. Context: An object’s behaviour needs to be extended. The new

    behaviour only applies to some instances. Forces Solution
  3. Context: An object’s behaviour needs to be extended. The new

    behaviour only applies to some instances. Forces: Addition of feature(s) should be invisible to client code. Base class should not know about additional behaviour. There are multiple independent extensions. Solution
  4. Solution: Wrap the object in a decorator. Implement or override

    feature-specific methods. Pass other messages to the wrapped object. Context: An object’s behaviour needs to be extended. The new behaviour only applies to some instances. Forces: Addition of feature(s) should be invisible to client code. Base class should not know about additional behaviour. There are multiple independent extensions.
  5. class LargeDrink def initialize drink @drink = drink end !

    def ingredients @drink.ingredients end ! def price @drink.price * BigDecimal("1.2") end end
  6. class MilkDrink def initialize drink @drink = drink end !

    def ingredients @drink.ingredients + [:milk] end ! def price @drink.price end end
  7. class MilkDrink def initialize drink @drink = drink end !

    def ingredients @drink.ingredients + [:milk] end ! def price @drink.price end end
  8. class MilkDrink def initialize drink @drink = drink end !

    def ingredients @drink.ingredients + [:milk] end ! def price @drink.price end end
  9. class MilkDrink def initialize drink @drink = drink end !

    def ingredients @drink.ingredients + [:milk] end ! def method_missing *args, &block @drink.send(*args, &block) end end
  10. class Decorator def initialize obj @obj = obj end !

    attr_reader :obj ! def method_missing *args, &block obj.send(*args, &block) end end
  11. class LargeDrink < Decorator def price obj.price * BigDecimal("1.2") end

    end ! class MilkDrink < Decorator def ingredients obj.ingredients + [:milk] end end
  12. require "delegate" ! class LargeDrink < SimpleDelegator def price super

    * BigDecimal("1.2") end end ! class MilkDrink < SimpleDelegator def ingredients super + [:milk] end end
  13. module LargeDrink def price super * BigDecimal("1.2") end end !

    module MilkDrink def ingredients super + [:milk] end end
  14. drink = Coffee.new drink.extend LargeDrink drink.extend MilkDrink ! drink.ingredients #

    => [:beans, :water, :milk] ! "%.2f" % drink.price # => "2.40"