Thursday, September 27, 2012

Favor Composition Over Inheritance

I have been reading "Head First Design Patterns" [1], which used Java to construct all examples. It's made me wonder what would it take to implement same pattern in Ruby. What would be the different, specially Ruby is not "type" language at all.

The first pattern that I am going to try is "Strategy" pattern. The principal behind "Strategy" pattern is "Favor composition over inheritance". It's interesting to see that at least two different way we can implement this principal in Ruby. One using module, another using class.

Let use the same design in the book, the duck design. Since all duck would have name, and can swim. The Duck base class would contain attribute name and method swim(). All duck swim, you know.
class Duck 
  attr_reader :name
  def initialize
    @name = "simple duck"
  end

  def swim
    puts "All duck can swim."
  end
end

However, not all duck are fly in the same way. MallarDuck and RedheadDuck will be able to fly with wing, while ToyDuck won't be able to quack or fly at all. To be able to share same behavior between MallardDuck and RedheadDuck, we create a module under then namespace Behavior
module Behavior
  module FlyWithWing
    def fly
      puts "Fly with wing"
    end
  end
end

module Behavior
  module NotFly
    def fly
      puts "no way I can fly"
    end
  end
end

class MallardDuck < Duck
  include Behavior::FlyWithWing

  def initialize
    @name = 'Mallard duck'
  end
end

class RedheadDuck < Duck
  include Behavior::FlyWithWing

  def initialize
    @name = 'Redhead duck'
  end
end

class ToyDuck < Duck
  include Behavior::NotFly
  def initialize
    @name = 'Toy duck'
  end
end

irb> ducks = [MallardDuck.new, RedHeadDuck.new, ToyDuck.new]
irb> ducks.each(&:fly)
Fly with wing
Fly with wing
no way I can fly
With proper directory setup, and follow convention. Ruby will be able to find the file that contain module and include it properly with no configuration at all.

In this case, each module would contain in each file under directory "behavior". By ruby convention, filename would have to match the module name. That mean the file would be name "fly_with_wing.rb", and "not_fly.rb"

We can also do the same with "quack" behavior, say Behavior::QuackLoudly, Behavior::NotQuack, etc.

As you can see, we can use ruby module to implement composition quite efficiently. It's seem natural to me to use module to break down object into different component and composed them.

Next time, I will describe more in detail of the implementation.

[1] Head First Design Patterns, Eric Freema, Elisabeth Freeman, O'Reilly 2004.

No comments:

Post a Comment