Strategy Design Pattern in Ruby
May 15th, 2006
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:
Here's the output generated by the file:
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.
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 |
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”
Sorry, comments are closed for this article.
June 10th, 2007 at 07:21 PM amazon:0596007124 doesn't work. I guess you have to fix your code ;-)
June 10th, 2007 at 07:21 PM 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...
June 10th, 2007 at 07:21 PM 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
June 10th, 2007 at 07:21 PM 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...
June 10th, 2007 at 07:21 PM 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.