Saturday, September 29, 2012

Favor Composition Over Inheritance - Part 3





In part 2, I implement Duck with fly behavior. Now, I am going show how to add new behavior, quack . Let say, I want to add two quack behavior, Behavior::QuackLoud and Behavior::QuackSilent. Similar to fly behavior, each behavior is implemented in module, and put under directory [behavior].

...
 |+[behavior]
   |-...
   |-quack_loud.rb
   |-quack_silent.rb
The Behavior::QuackLoud is implement in quack_loud.rb module as shown here:
module Behavior
  module QuackLoud
    def quack
      puts "quack loud"
    end
  end
end
It's not hard to see how Behavior::QuackSilent is implemented.

Now, I want MallardDuck, which already be able to fly with wing, also can quack loudly. All I have to do is include Behavior::QuackLoud in the class MallardDuck. Of course, I need to require appropriate file:
require './duck'
require './behavior/fly_with_wing'
require './behavior/quack_loud'

class MallardDuck < Duck
  include Behavior::FlyWithWing
  include Behavior::QuackLoud
  ...
end

In the same way, I can define duck that can fly but not quack, or duck that can not fly but quack loudly. Different duck with different behaviors can be defined by including appropriate behavior module. A particular behavior defined in one place, and can be easily shared between different class of duck. As you can see, it's easy to understand, implement, and maintain.

Let recap a bit: Share behavior of all kind of Duck are implemented in base class. Then different behavior are separated out into modules, and then get included into class definition when that class need  it. We can say, duck class is composed with different behavior module.

Here, how the composition is done in ruby using module!

Next time, I will use different way of implementation to allow a duck to change behavior during run time. It's would be fun!