As I'm reading through Head First Design Patterns, I've decided to write some sample ruby code for each pattern. This will certainly be educational for me, and it might prove useful for some of my readers. As always, I am not an expert in ruby by any means, so if I flub up my code or if there's a better way to do something, please leave a comment and illuminate me. So, here we go!

From the book: "The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

So, I'm going to more or less reproduce the book's example of a duck simulation program. Each duck exhibits various behaviors, and we want a way to easily encapsulate each behavior and make related behaviors interchangeable. For example, some ducks fly and some don't. Decoy ducks just float in the water. Some ducks have a normal quack, but a decoy makes no sound and rubber ducks tend to squeak.

The book has its examples coded in java. Java implementations require the create of an abstract class to derive ducks from and the creation of interfaces to represent the various behaviors. You can find the java code for all of their examples by clicking this link.

In ruby, we have neither abstract base classes or interfaces, nor do we need them. We have a simpler, and perhaps, more elegant solution to the problem. First, let's take a look at my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

module FlyWithWings
  def fly()
    puts "I'm flying Yay!"
  end
end

module FlyNoWay
  def fly()
    puts "I can't fly!"
  end
end

module Quack
  def quack()
    puts "Quack!"
  end
end

module Squeak
  def quack()
    puts "Squeak!"
  end
end

module MuteQuack
  def quack()
    puts "<<Silence>>"
  end
end

module FlyWithRocketPower
  def fly()
    puts "I'm flying with rockets!"
  end
end

class Duck
  include FlyWithWings
  include Quack
  def sayHello()
    puts "Hi!  I'm a generic duck!"
  end
  def BeADuck()
    self.sayHello
    fly
    quack
  end
end

class MallardDuck < Duck
  include FlyWithWings
  include Quack
  def sayHello()
    puts "Hullo!  I'm a mallard!"
  end
end

class ModelDuck < Duck
  include FlyNoWay
  include MuteQuack
  def sayHello()
    puts "Howdy!  I'm a model duck!"
  end
end

newDuck = Duck.new
newDuck.BeADuck

mallard = MallardDuck.new
mallard.BeADuck

modelDuck = ModelDuck.new
modelDuck.BeADuck

#Oh noes!  ModelDuck has to make it to LA in 10 minutes!!
modelDuck.extend FlyWithRocketPower
modelDuck.extend Squeak
modelDuck.BeADuck
Here's the output generated by the file:

Hi!  I'm a generic duck!
I'm flying Yay!
Quack!
Hullo!  I'm a mallard!
I'm flying Yay!
Quack!
Howdy!  I'm a model duck!
I can't fly!
<>
Howdy!  I'm a model duck!
I'm flying with rockets!
Squeak!
Here I've used the power of ruby modules and mixins to do almost all of the work for me. I create a seperate module for each behavior (my algoritms in this case) and then create methods for either "fly" or "quack." In order to dynamically change the modelDuck's behavior, I use the extend method which mixes in the named module into the class, replacing the previous fly and/or quack behaviors.

For the heck of it, I created a Duck superclass containing a method for each duck to announce itself and its abilities. Down where we're running our test, we create a few ducks. Then we switch out the behavior of the modelDuck so that it squeaks and flys with rocket power.

This is the strength of the strategy design pattern. As circumstances dictate, our objects can change their behaviors as needed. We took a lame duck and attached rockets to him with a single line of code. Ruby is awesome!

BONUS: Here's another site I found with yet another way to do the strategy pattern in ruby.

5 Responses to “Strategy Design Pattern in Ruby”

  1. John Bokma Says:
    amazon:0596007124 doesn't work. I guess you have to fix your code ;-)
  2. TAD Says:
    Sorry, it looks like typo has decided that I can no longer use the Amazon post processor filter. I'll have to fool around with it some more later...
  3. Edgar Says:
    We noticed that your blog have Ruby and Ruby On Rails releated content, and that is why we would like to invite you to register yourself and your blog at RubyCorner, a directory of Ruby related blogs: http://www.rubycorner.com/ We like to think about RubyCorner as a "meeting place" for the community, also as a "focal point" where the people new to this technology can quickly tune into the pulse of the community. Registering your blog will help build a valuable resource for this growing community
  4. Tyler K Says:
    i literally had a dream about this yesterday. I'm happy to help out if I can help out in any way. I'll be reading...
  5. Eric Hutzelman Says:
    I was playing around with this and ran into a small problem. I'm new to ruby, but it looks like if you try to extend back to a previous behavior after using extend to get a new behavior, the previous behavior will not show up. For example, if at the end of your test, you wanted your model duck to have its Quack behavior restored to MuteQuack (modelDuck.extend MuteQuack) and then ran your BeADuck method, the modelDuck would still "Squeak". I'm guessing this is because Ruby knows that modelDuck has previously been extended by MuteQuack and doesn't try to extend it again? If that's the case, then the strategy pattern seems a little crippled using this approach.

Sorry, comments are closed for this article.