To support this feature, the base class, now, hold all the behaviors implemented for subclass. That quite different from using module, which the base class know nothing about subclass behaviors at all.
class Duck attr_reader :name attr_accessor :fly_behavior, :quack_behavior def initialize @name = "mallard duck" @fly_behavior = nil @quack_behavior = nil end def fly fly_behavior.fly end def quack quack_behavior.quack end end
In this case, the base class by default has no behaviors. It's the subclass job to create, and assign default behaviors of the subclass when initiate an instance. For example, MallardDuck default behavior for flying is fly with wing, etc.
require './duck' require './behavior/fly_with_wing' require './behavior/quack_loud' class MallardDuck < Duck def initialize @name = "mallard duck" @fly_behavior = Behavior::FlyWithWing.new @quack_behavior = Behavior::QuackLoud.new end end
For the behaviors, instead of module, it's become a class.
module Behavior class FlyWithWing def fly puts 'Fly with wing.' end end end
Here an implementation of duck_app.rb. This example show an instance of MallardDuck, which first can fly and then the same duck can not fly any more when the behavior re-assigned. It still quack loadly thou.
require './mallard_duck' require './behavior/not_fly' mduck = MallardDuck.new p mduck.name mduck.fly mduck.fly_behavior = Behavior::NotFly.new mduck.fly mduck.quack
Here you have it! A duck that can change behavior at run time.
This implementation of composition is quite similar to java "interface" implementation using in the head first design pattern book . To be almost exactly the same, we would duplicate fly, and quack method all over the sub-classes. That is some how a give and a curse of ruby!
I still one more to go on this topic, a comparison for module and object approaches. Stay tuned!
 Head First Design Patterns, Eric Freema, Elisabeth Freeman, O'Reilly 2004.
Post a Comment