Comparing Design Patterns in Ruby and C#: The Strategy Pattern

Home/.Net, Ruby/Comparing Design Patterns in Ruby and C#: The Strategy Pattern

In the previous post of this series, we looked at how the Template pattern is implemented in both Ruby and C#. In this post, we’ll take a look at the Strategy pattern…one of my favorites.

In it’s classic form, the Strategy pattern consists of a context class and various “strategies” which share a common interface. The context class is given a strategy to which it can delegate work to. Think of it as passing an algorithm to a class. To change the way the class works, change the algorithm you feed it. This makes it really easy to adhere to the open-closed principle which states that an object should be open for extension but closed for modification. We can drastically change how our class operates without changing a line of code in the class itself.

In .Net, the Sort method on generic lists is a perfect example of how the Strategy pattern can be applied. To change how the list is sorted, you simply pass in a method that matches the Comparison<T> delegate or a class that implements IComparer<T>. (See my earlier post on the various ways you can sort a generic list in .Net).

Ruby has a really cool example built in as well with it’s rdoc utility which uses strategies both for distilling documentation from various languages (C, Ruby, etc) and in how it ouputs the documentation (HTML, XML, CHM).

In our examples below, we’re creating a Driver object that has three different properties: DrivingHabit, MilesDriven, and SpeedingTickets. It also has a method “Drive” which accepts the number of hours that the driver should drive as a parameter. The Drive method delegates the actual "driving” logic to the driving habit that it is composed with. A cautious driver follows the speed limit (55mph) and never gets a speeding ticket. A reckless driver, however, travels along at 80mph and gets a speeding ticket every 1/2 hour.

C# Example

For the Strategy pattern to work in a static language, we must first define a contract that the strategies themselves must adhere to as well as the context class. So we’ll start by creating the Driver object and defining the IDrivingHabit interface.

 
namespace DesignPatterns.StategyPattern
{
    public class Driver
    {
        public IDrivingHabit DrivingHabit { get; set; }
        public int MilesDriven { get; set; }
        public int SpeedingTickets { get; set; }

        public Driver(IDrivingHabit drivingHabit)
        {
            DrivingHabit = drivingHabit;
        }

        public void Drive(int hours)
        {
            DrivingHabit.Drive(this, hours);
        }
    }
}
 
namespace DesignPatterns.StategyPattern
{
    public interface IDrivingHabit
    {
        void Drive(Driver driver, int duration);
    }
}

Now let’s create our strategies.

namespace DesignPatterns.StategyPattern
{
    public class CautiousDrivingHabit : IDrivingHabit
    {
        public void Drive(Driver driver, int hours)
        {
            driver.MilesDriven =  hours * 55;
        }
    }
}
namespace DesignPatterns.StategyPattern
{
    public class RecklessDrivingHabit : IDrivingHabit
    {
        public void Drive(Driver driver, int hours)
        {
            driver.MilesDriven += hours * 80;
            driver.SpeedingTickets += hours * 2;
        }
    }
}

And to see how this would actually be used, we’ll take a look at the unit tests.

using NUnit.Framework;

namespace DesignPatterns.StategyPattern
{
    [TestFixture]
    public class When_a_cautious_driver_is_on_the_road_for_3_hours
    {
        private Driver granny;

        [SetUp]
        public void EstablishContext()
        {
            granny = new Driver(new CautiousDrivingHabit());
            granny.Drive(3);
        }

        [Test]
        public void Should_move_a_total_of_165_miles()
        {
            Assert.That(granny.MilesDriven, Is.EqualTo(165));
        }

        [Test]
        public void Should_not_receive_any_speeding_tickets()
        {
            Assert.That(granny.SpeedingTickets, Is.EqualTo(0));
        }
    }

    [TestFixture]
    public class When_a_reckless_driver_is_on_the_road_for_3_hours
    {
        private Driver speedRacer;

        [SetUp]
        public void EstablishContext()
        {
            speedRacer = new Driver(new RecklessDrivingHabit());
            speedRacer.Drive(3);
        }

        [Test]
        public void Should_move_a_total_of_240_miles()
        {
            Assert.That(speedRacer.MilesDriven, Is.EqualTo(240));
        }

        [Test]
        public void Should_receive_a_speeding_ticket_for_every_half_hour_on_the_road()
        {
            Assert.That(speedRacer.SpeedingTickets, Is.EqualTo(6));
        }
    }
}

Pretty simple, eh? Let’s take a look at how we would do this in Ruby.

Ruby Example

First, we’ll create the Driver object. So far doesn’t seem that much different from the C# example.

class Driver
  attr_accessor :driving_habit, :miles_driven, :speeding_tickets

  def initialize(driving_habit)
    @driving_habit = driving_habit
    @miles_driven = 0
    @speeding_tickets = 0
  end

  def drive(hours)
    @driving_habit.drive self, hours
  end
end

Next we’re going to jump straight into creating our Strategy classes. Since Ruby supports duck typing, we really have no need to create an object that defines what our strategies should look like. We could define a base DrivingHabit object that included a Drive method that we can override in our implementations, but that would be producing extra code for no added benefit. We’re ok as long as the object we pass in has a Drive method that matches the required signature.

class CautiousDrivingHabit
  def drive(driver, hours)
    driver.miles_driven += hours * 55
  end
end
require 'lib/driver'

class RecklessDrivingHabit
  def drive(driver, hours)
    driver.miles_driven += hours * 80
    driver.speeding_tickets += hours * 2
  end
end

And now we’ll take a look at the corresponding unit tests.

require 'lib/cautious_driving_habit'

describe "When a cautious driver is on the road for 3 hours" do
  before(:each) do
    @granny = Driver.new(CautiousDrivingHabit.new)
    @granny.drive 3
  end

  it "should move a total of 165 miles" do
    @granny.miles_driven.should == 165
  end

  it "should not receive any speeding tickets" do
    @granny.speeding_tickets.should == 0
  end
end
require 'lib/reckless_driving_habit'

describe "When a reckless driver is on the road for 3 hours" do
  before(:each) do
    @speed_racer = Driver.new(RecklessDrivingHabit.new)
    @speed_racer.drive 3
  end

  it "should move a total of 240 miles" do
    @speed_racer.miles_driven.should == 240
  end

  it "should not receive a speeding ticket for every half hour on the road" do
    @speed_racer.speeding_tickets.should == 6
  end
end

Simplifying the Strategy Pattern

For simple scenarios (such as this one) where the strategies are extremely basic, we can actually reduce the need for the strategy classes themselves. In C#, we just need to add an overloaded Drive method that accepts a delegate that matches the Drive method in the IDrivingHabit interface. (We could have changed the contructor to do this instead.)

public void Drive(int hours, Action<Driver, int> drivingHabit)
{
     drivingHabit(this, hours);
}

And to use it, we pass a lambda expression in that contians our logic.

speedRacer.Drive(3, (driver, hours) =>
                     {
                          driver.MilesDriven += hours*80;
                          driver.SpeedingTickets += hours*2;
                     });

We can do something very similiar in Ruby. We’ll create a new method that accepts a code block as a second parameter. Note that we had to create a new name for this method (Ruby doesn’t support method overloading).

def drive_using_habit(hours, &amp;amp;driving_habit)
 driving_habit.call self, hours
end

And the code using this.

@speed_racer.drive_using_habit(3) do |driver, hours|
    driver.miles_driven += hours * 80
    driver.speeding_tickets += hours * 2
end

In the next post, we’ll take a look at a very similiar pattern, the Observer pattern.

kick it on DotNetKicks.com

About the Author:

Freelance software developer in the Cleveland area.

2 Comments

  1. Jon Kruger July 8, 2009 at 12:36 pm - Reply

    This is awesome stuff. I don’t know a ton about Ruby and I’ve always wondered how different patterns are done in Ruby. Good to see the tests too! Looking forward to the rest of the series.

  2. Darrell Mozingo July 8, 2009 at 6:33 pm - Reply

    It is pretty neat seeing examples side by side in different languages. We’re working through the Ruby Koans at our bi-weekly get togethers then moving onto a Rails app. I’m sure these posts will come in handy!

    Keep it up John.

Leave A Comment