Head First Design Patterns in Ruby, Chapter 1

Overview

Source code: ruby-hfdp_ch01.tar.gz

Pattern: STRATEGY

Concepts: Delegation

Principles:

  1. Encapsulate what varies.
  2. Program to interfaces, not implementations.
  3. Favor composition over inheritance.

Some initial thoughts on Java and Ruby

One of the first ideas introduced in HFDP is to “program to a supertype”. Which means a clearly defined, and enforced by definition in interface or abstract class, set of methods. This is best done in Ruby with mixins. Instead of several different implements of a given interface, I will create several modules implementing a given set of methods. There is no compile-time enforcement here, as in Java; if I forget a method in a given module implementation, I won’t know until runtime.

A first attempt, hewing close to Java

But first let’s try to approximate the Java version of the first HFDP example as closely as possible. I’ll use the technique to simulate “abstract” classes used in Ruby Cookbook.

duck_java.rb

   1  class Duck
   2    def initialize
   3      raise NotImplementedError
   4    end
   5    def display
   6      raise NotImplementedError
   7    end
   8    def perform_fly
   9      @fly_behaviour.fly()
  10    end
  11    def perform_quack
  12      @quack_behaviour.quack()
  13    end
  14    def swim
  15      "All ducks float, even decoys!"
  16    end
  17  end
  18  
  19  class FlyBehaviour
  20    def initialize
  21      raise NotImplementedError
  22    end
  23    def fly
  24      raise NotImplementedError
  25    end
  26  end
  27  
  28  class QuackBehaviour
  29    def initialize
  30      raise NotImplementedError
  31    end
  32    def fly
  33      raise NotImplementedError
  34    end
  35  end
  36  
  37  class FlyWithWings < FlyBehaviour
  38    def initialize
  39    end
  40    def fly
  41      "I'm flying!"
  42    end
  43  end
  44  
  45  class FlyNoWay < FlyBehaviour
  46    def initialize
  47    end
  48    def fly
  49      "I can't fly."
  50    end
  51  end
  52  
  53  class Quack < QuackBehaviour
  54    def initialize
  55    end
  56    def quack
  57      "Quack"
  58    end
  59  end
  60  
  61  class MuteQuack < QuackBehaviour
  62    def initialize
  63    end
  64    def quack
  65      "<< Silence >>"
  66    end
  67  end
  68  
  69  class Squeak < QuackBehaviour
  70    def initialize
  71    end
  72    def quack
  73      "Squeak"
  74    end
  75  end
  76  
  77  class MallardDuck < Duck
  78    def initialize
  79      @fly_behaviour = FlyWithWings.new
  80      @quack_behaviour = Quack.new
  81    end
  82  
  83    def display
  84      "I'm a real Mallard Duck"
  85    end
  86  end

Now, this is obviously ridiculous, but bear with me. The NotImplementedError exceptions are there to simulate methods defined in a Java abstract class. If they aren’t implemented in a subclass, when they are invoked in an instance of the subclass an exception will be raised. Note that this will all happen at runtime, so the compiler won’t complain about such a thing. Also, by including this exception as the result of initialize(), we prevent the instantiation of these “abstract” classes. We’ll have to define at least an empty initialize() in subclasses that are not “abstract”.

Okay, so our “abstract” superclass Duck defines “abstract” initialize() and display() methods, along with an implemented swim(). Flying and Quacking behaviours are encapsulated into separate FlyBehaviour and QuackBehaviour “abstract” classes at lines 19 and 28. In Java, these are interface types that are declared in Duck, but in Ruby we don’t have declare types; type is determined by what methods the value of a variable can respond to at runtime.

At line 37 and 45 we implement a couple of FlyBehaviour by subclassing it to FlyWithWings and FlyNoWay, and likewise three QuackBehaviour.

Finally at line 77 we subclass Duck to MallardDuck. In initialize() we instantiate the FlyBehaviour and QuackBehaviour subclasses we want into instance variables @fly_behaviour and @quack_behaviour. Here is where some dynamic Ruby magic comes in: we used these instance variables in the Duck superclass methods perform_fly and perform_quack, though Duck knew nothing of them beforehand. In Java, this is accomplished by declaring them as supertypes to keep the compiler happy, then using polymorphism to set the subtype at runtime. In Ruby not only do we not have such a type system, but there is no declaration at all; Duck only cares that whatever lives in @fly_behaviour and @quack_behaviour can respond to fly() and quack() at runtime.

So even in our best simulation of Java’s abstract types we’re getting pretty dynamic!

A second attempt, still using classes

Well, that was pretty convoluted. In practice you Just Don’t simulate the “abstract” in a dynamic, duck-typed language like Ruby. So let’s toss all that and refactor to a more Ruby-looking bit of code, but still using only classes.

duck_ruby_classes.rb

   1  class Duck 
   2    def perform_fly
   3      @fly_behaviour.fly()
   4    end
   5    def perform_quack  
   6      @quack_behaviour.quack()
   7    end
   8    def swim
   9      "All ducks float, even decoys!"
  10    end
  11  end
  12  
  13  class FlyWithWings 
  14    def fly
  15      "I'm flying!"
  16    end
  17  end
  18  
  19  class FlyNoWay 
  20    def fly
  21      "I can't fly."
  22    end
  23  end
  24  
  25  class Quack 
  26    def quack
  27      "Quack"
  28    end
  29  end
  30  
  31  class MuteQuack 
  32    def quack
  33      "<< Silence >>"
  34    end
  35  end
  36  
  37  class Squeak 
  38    def quack
  39      "Squeak"
  40    end
  41  end
  42  
  43  class MallardDuck < Duck
  44    def initialize
  45      @fly_behaviour = FlyWithWings.new    
  46      @quack_behaviour = Quack.new    
  47    end
  48    def display
  49      "I'm a real Mallard Duck"
  50    end
  51  end

Okay, it’s cleaner. It’s still not very Rubyish, though. We’re using classes inappropriately; they’re just providing different versions of certain methods. Far better is to use mixins!

Class mixins with include

duck_ruby_mixins.rb

   1  class Duck 
   2    def perform_fly
   3      fly()
   4    end
   5    def perform_quack  
   6      quack()
   7    end
   8    def swim
   9      "All ducks float, even decoys!"
  10    end
  11  end
  12  
  13  module FlyWithWings 
  14    def fly
  15      "I'm flying!"
  16    end
  17  end
  18  
  19  module FlyNoWay 
  20    def fly
  21      "I can't fly."
  22    end
  23  end
  24  
  25  module Quack 
  26    def quack
  27      "Quack"
  28    end
  29  end
  30  
  31  module MuteQuack 
  32    def quack
  33      "<< Silence >>"
  34    end
  35  end
  36  
  37  module Squeak 
  38    def quack
  39      "Squeak"
  40    end
  41  end
  42  
  43  class MallardDuck < Duck
  44    include FlyWithWings
  45    include Quack
  46  
  47    def display
  48      "I'm a real Mallard Duck"
  49    end
  50  end

Splendid! This feels much better. And in practice, we could bundle up the various Fly and Quack modules into a single file, require that file, than include whatever modules are appropriate!

So in Ruby it looks like mixins are the way to go when we want to delegate methods with the STRATEGY pattern.

But it gets even better! The next step in HFDP is to be able to set a given Duck subclass’s flying and quacking at runtime, rather than hardcode it as we’ve done with MallardDuck. Again, in Java our abstract supertype gives us the type to use as our setter method argument, but we can skip that in Ruby…and use super-dynamic features to just pass it a string!

duck_ruby_mixins_dynamic_class_eval.rb

   1  class Duck
   2    def setFlyBehaviour(behaviour)
   3      self.class.class_eval("include #{behaviour}")
   4    end
   5    def setQuackBehaviour(behaviour)
   6      self.class.class_eval("include #{behaviour}")
   7    end
   8    def perform_fly
   9      fly()
  10    end
  11    def perform_quack
  12      quack()
  13    end
  14    def swim
  15      "All ducks float, even decoys!"
  16    end
  17  end

Take the include statements out of the MallardDuck subclass, and then do something like this:

   1  mallard = MallardDuck.new
   2  mallard.setFlyBehaviour("FlyNoWay")
   3  mallard.setQuackBehaviour("Quack")

Very cool. But…eval is not always such a good idea. When I first started using Ruby, I thought you could do something like include behaviour, but you can’t include on the fly like that. Hence the class_eval above. So there are some limits to Ruby’s dynamic nature… and we’ll have to rethink this code.

Object mixins with extend

Ruby provides the extend method to mixin to an object at runtime, and after all, we want to add behaviours to a specific instance of a class, not to a class itself. So the restriction on include is appropriate, as long as we have extend:

   1  mallard = MallardDuck.new
   2  mallard.extend(FlyNoWay)
   3  mallard.extend(Quack)

And this is exactly what we wanted. But in Java we had the indirect perform_* methods for fly() and quack() in the Duck superclass because we have to get at the *Behaviour interfaces indirectly. In Ruby, we’re adding methods fly() and quack() directly to an object, then invoking them via methods of that object’s superclass! There’s nothing to prevent us from saying mallard.fly(). So for the final, full example from chapter 1 let’s scrap that and do it all Rubylike:

duck_final.rb

   1  module FlyWithWings 
   2    def fly
   3      "I'm flying!"
   4    end
   5  end
   6  
   7  module FlyNoWay 
   8    def fly
   9      "I can't fly."
  10    end
  11  end
  12  
  13  module Quack 
  14    def quack
  15      "Quack"
  16    end
  17  end
  18  
  19  module MuteQuack 
  20    def quack
  21      "<< Silence >>"
  22    end
  23  end
  24  
  25  module Squeak 
  26    def quack
  27      "Squeak"
  28    end
  29  end
  30  
  31  class Duck 
  32    def swim
  33      "All ducks float, even decoys!"
  34    end
  35  end
  36  
  37  class MallardDuck < Duck
  38    include FlyWithWings
  39    include Quack
  40    def display
  41      "I'm a real Mallard Duck"
  42    end
  43  end
  44  
  45  class ModelDuck < Duck
  46    include FlyNoWay
  47    include Quack
  48    def display
  49      "I'm a model duck"
  50    end
  51  end
  52  
  53  module FlyRocketPowered
  54    def fly
  55      "I'm flying with a rocket!" 
  56    end
  57  end  

and we can use this like so

   1  model = ModelDuck.new
   2  model.fly # => "I can't fly."
   3  model.extend(FlyRocketPowered)
   4  model.fly # => "I'm flying with a rocket!"

Here, our ModelDuck class has some default behaviours via include, but they are overridden at runtime with extend, and we forget about the perform_* indirection. Now that’s Ruby!

A final note on the code here. Above is the complete duck_final.rb, and we test it using Rake to run tests in tests/test_duck_final.rb. Here’s the directory layout for hfdp-ruby/ch01:

Rakefile
duck_final.rb
duck_java.rb
duck_ruby_classes.rb
duck_ruby_mixins.rb
duck_ruby_mixins_dynamic_class_eval.rb
duck_ruby_mixins_dynamic_extends.rb
test/

and under test/ is

test_duck_final.rb
test_duck_java.rb
test_duck_ruby_classes.rb
test_duck_ruby_mixins.rb
test_duck_ruby_mixins_dynamic_class_eval.rb
test_duck_ruby_mixins_dynamic_extends.rb

Each of the duck_* files is an iteration of Duck as discussed above. To exercise each of these, rake runs the corresponding test_*. Here’s the Rakefile:

   1  require 'rake/testtask'
   2  
   3  tests = ['test_duck_java','test_duck_ruby_classes',
   4            'test_duck_ruby_mixins', 
   5            'test_duck_ruby_mixins_dynamic_class_eval',
   6            'test_duck_ruby_mixins_dynamic_extends',
   7            'test_duck_final']
   8  
   9  tests.each do |test|
  10    Rake::TestTask.new test do |t|
  11      t.name = test
  12      t.pattern = 'test/' + test + '.rb'
  13      t.warning = true
  14      t.verbose = true
  15    end
  16  end
  17  
  18  task 'default' => tests

Note that we could have defined a single pattern test_* to run all the tests as one task, but that would run them all in the same instance of the Ruby interpreter, and we couldn’t be sure that definitions in earlier iterations were not carrying over into the latest. Actually, this wouldn’t be a problem as Ruby lets you do nearly anything at runtime, except that we couldn’t be sure that we weren’t missing definitions in the current iteration. By defining separate tasks for each iteration we guarantee that we’ll have a new ruby each time.

Since all the tests are the default task, to run them, in the top-level directory just do

   1  % rake

Let’s look at the test_duck_final.rb test code.

   1  require 'test/unit'
   2  require 'duck_final'
   3  
   4  class DuckFinalTest < Test::Unit::TestCase
   5  
   6  def setup
   7    @mallard = MallardDuck.new
   8    @model = ModelDuck.new
   9  end
  10  
  11  def test_new_mallard
  12    assert_equal "MallardDuck", @mallard.class.to_s
  13  end
  14  
  15  def test_new_model
  16    assert_equal "ModelDuck", @model.class.to_s
  17  end
  18    
  19  def test_mallard_quack
  20    assert_equal "Quack", @mallard.quack
  21  end
  22  
  23  def test_mallard_fly
  24    assert_equal "I'm flying!", @mallard.fly
  25  end
  26  
  27  def test_model_fly
  28    assert_equal "I can't fly.", @model.fly
  29  end
  30  
  31  def test_model_rocket_fly
  32    @model.extend(FlyRocketPowered)
  33    assert_equal "I'm flying with a rocket!", @model.fly
  34  end
  35  
  36  end # DuckFinalTest

At line 2, we include the appropriate iteration. Then we define a class DuckFinalTest that subclasses Test::Unit::TestCase. The setup() method at line 6 defines a method that will be run for each of the test_*() methods that follow; note that changes made within one test_*() method to an object instantiated in setup() will not be visible in another test_*() method! The setup() task is run anew for each.

Rather than printing things to the screen as in HFDP, our methods return values, which allows us to test them programmatically. We use the Ruby expression-oriented idiom here of having bare strings at the end of our methods; in future we may use the implicit return values of other kinds of expressions.

You can see the joy of testing already: we can mess around with our code, and then just rerun the tests! The assert_equal Test::Unit method will do the “looking” at the “output” for us, letting us know if something unexpected happened.

In later chapters, we’ll explore Test::Unit further, along with ZenTest, RSpec, rcov, and other tools.

A look back

Have we cheated by extending our objects at runtime, adding methods that we call directly on the object? Well, the idea was to encapsulate behaviours so that we could easily create new ones and specify them to our objects at runtime, and that’s exactly what we’ve done.

We get a kind of abstraction, in that modules can’t be instantiated like classes, and they cannot keep state, although they can reference instance variables of the class they’re mixed in to, although we haven’t done that here (and it breaks the encapsulation we’re going for). Oh, in Ruby they can even create instance variables that will be instance variables of the class they’re mixed in to, a little like the variables referenced in Duck methods that are defined in its subclasses.

This might better be viewed as the restricted version of multiple inheritance that Matz calls all this “single inheritance with implementation sharing”. (RW2:rw2).

An excursion into Ruby singletons

In Ruby, when we use extend as above, an anonymous singleton class subclassing the object’s original parent class is created for the object, and the module methods are mixed in to that class, of which our object is an instance. These methods are then singleton instance methods of that singleton class.

This can be used to add methods to an object outside of extend:

   1  foo = Bar.new
   2  
   3  def foo.baz
   4    ...
   5  end
   6  
   7  foo.bar

which is the same as

   1  foo = Bar.new  
   2  
   3  class << foo
   4    def baz
   5      ...
   6    end
   7  end
   8  
   9  foo.bar

Method baz is defined for object foo in both examples above, but the second is a little more explicit: an anonymous singleton class that is a subclass of foo class Bar is created for object foo; an instance method is defined for that class which is now available to the only object of that class, foo. The singleton nature of this class is apparent here: we cannot instantiate it as it is anonymous and has no object reference.

An interesting sidenote in Ruby is that a class is an object of class Class. A class method is implemented as above, so the traditional way of defining class methods

   1  class SomeClass
   2    def SomeClass.class_method
   3      ...
   4    end
   5  end

is the same as

   1  class SomeClass
   2    class << self
   3      def class_method
   4        ...
   5      end
   6    end
   7  end

Here, an anonymous singleton subclass of class Class is created for the Class object SomeClass, so we can call the instance method class_method on the object SomeClass as SomeClass.class_method.

Well, sort of…notice that if we do foo.class we still get Bar, not a reference to the singleton class, so it’s a little more complicated than I’ve explained, but now we’re getting far afield…

There is one important caveat: singleton objects cannot be serialized with Marshal.