Most of my career has been in .Net development and I’m pretty comfortable applying design patterns in C#, but as I’m learning Ruby, I was finding it difficult to figure out how to implement them without creating awkward, hard-to-read code. Recently a local Ruby guru, Nate Klaiber, recommended that I pick up the book Design Patterns in Ruby. Well, he more than recommended it, he actually gave a copy autographed by the author (totally unexpected but very, very appreciated!). He has a thorough review of the book (and many others) on his book review site.

As I read through the book, I’m going to do a series of posts showing examples of how each pattern would be applied in both C# and Ruby. (The book has around a dozen patterns so this will probably take several weeks to get through them all).

The first one we’re going to work through is the Template pattern, which is arguably one of the easiest patterns to learn. The template pattern involves creating a base class with methods that can be overridden by subclasses. The base class itself has a central method that will call the other methods in the object.

In the examples below, we’re going to create two vehicle factory classes, one that creates a vehicle specifically for winter travelling, and another that creates vehicles for summertime cruising.

*Note: I’m certainly no Ruby expert (or C# expert for that matter), but am learning here as I go. If you have a better way to do something, please leave a comment!

C# Example

The template method is all over the .Net framework. A perfect example is ASP.Net’s page lifecycle. You’re given hooks to perform actions during page load, init, etc.

Let’s start by creating our Vehicle class.

[source language=’csharp’] using System.Collections.Generic;

namespace DesignPatterns.TemplatePattern
{
public class Vehicle
{
public string SteeringWheel { get; set; }
public string Tires { get; set; }
public string Seats { get; set; }
public string Engine { get; set; }
public string MakeModel { get; set; }
public IList Amenities { get; set; }

public Vehicle()
{
Amenities = new List();
}
}
}[/source]

Next we’ll create our base class. Note that CreateVehicle() is the only public method. The rest are only accessible to objects that derive from this class. And some of the “hook” methods have a default implementation while others are declared abstract and must be overridden by the subclass.

[source language=’csharp’] namespace DesignPatterns.TemplatePattern
{
public abstract class VehicleFactoryBase
{
protected Vehicle VehicleUnderConstruction { get; set; }

protected virtual void AddSteeringWheel()
{
VehicleUnderConstruction.SteeringWheel = “Standard Steering Wheel”;
}

protected virtual void AddTires()
{
VehicleUnderConstruction.Tires = “All Season Tires”;
}

protected virtual void AddSeats()
{
VehicleUnderConstruction.Seats = “Cloth Bucket Seats”;
}

protected virtual void AddAmenities()
{

}

protected abstract void AddEngine();

protected abstract void AddMakeModel();

public Vehicle CreateVehicle()
{
VehicleUnderConstruction = new Vehicle();
AddSteeringWheel();
AddTires();
AddSeats();
AddAmenities();
AddEngine();
AddMakeModel();
return VehicleUnderConstruction;
}
}
}[/source]

Now we’re ready to create our first implementation, a vehicle factory that creates vehicles designed for living in the snow belt.

[source language=’csharp’] namespace DesignPatterns.TemplatePattern
{
public class WinterVehicleFactory : VehicleFactoryBase
{
protected override void AddTires()
{
VehicleUnderConstruction.Tires = “Snow Tires”;
}

protected override void AddAmenities()
{
VehicleUnderConstruction.Amenities.Add(“4 Wheel Drive”);
VehicleUnderConstruction.Amenities.Add(“Snow Plow”);
}

protected override void AddEngine()
{
VehicleUnderConstruction.Engine = “4.7L V-8”;
}

protected override void AddMakeModel()
{
VehicleUnderConstruction.MakeModel = “Jeep Grand Cherokee”;
}
}
}[/source]

Our second implementation will be a vehicle factory that creates our summer cruiser.

[source language=’csharp’] namespace DesignPatterns.TemplatePattern
{
public class SummerVehicleFactory : VehicleFactoryBase
{
protected override void AddSeats()
{
VehicleUnderConstruction.Seats = “Leather Bucket Seats”;
}

protected override void AddAmenities()
{
VehicleUnderConstruction.Amenities.Add(“Premium Sound System”);
}

protected override void AddEngine()
{
VehicleUnderConstruction.Engine = “3.7L V6”;
}

protected override void AddMakeModel()
{
VehicleUnderConstruction.MakeModel = “Nissan 370Z Coupe”;
}
}
}[/source]

And finally, below are the unit tests needed to verify both factories creates their vehicles as expected.

[source language=’csharp’] using NUnit.Framework;

namespace DesignPatterns.TemplatePattern
{
[TestFixture] public class When_creating_a_winter_vehicle
{
private Vehicle winterVehicle;

[SetUp] public void EstablishContext()
{
winterVehicle = new WinterVehicleFactory().CreateVehicle();
}

[Test] public void Should_create_a_Jeep_Grand_Cherokee()
{
Assert.That(winterVehicle.MakeModel, Is.EqualTo(“Jeep Grand Cherokee”));
}

[Test] public void Should_have_a_standard_steering_wheel()
{
Assert.That(winterVehicle.SteeringWheel, Is.EqualTo(“Standard Steering Wheel”));
}

[Test] public void Should_have_snow_tires()
{
Assert.That(winterVehicle.Tires, Is.EqualTo(“Snow Tires”));
}

[Test] public void Should_have_a_V8_engine()
{
Assert.That(winterVehicle.Engine, Is.EqualTo(“4.7L V-8”));
}

[Test] public void Should_have_standard_cloth_seats()
{
Assert.That(winterVehicle.Seats, Is.EqualTo(“Cloth Bucket Seats”));
}

[Test] public void Should_have_4_wheel_drive()
{
Assert.That(winterVehicle.Amenities.Contains(“4 Wheel Drive”));
}

[Test] public void Should_have_a_snow_plow()
{
Assert.That(winterVehicle.Amenities.Contains(“Snow Plow”));
}

[Test] public void Should_not_have_any_other_amenities()
{
Assert.That(winterVehicle.Amenities.Count, Is.EqualTo(2));
}
}
}[/source]

[source language=’csharp’] using NUnit.Framework;

namespace DesignPatterns.TemplatePattern
{
[TestFixture] public class When_creating_a_summer_vehicle
{
private Vehicle summerVehicle;

[SetUp] public void EstablishContext()
{
summerVehicle = new SummerVehicleFactory().CreateVehicle();
}

[Test] public void Should_create_a_Nissan_370Z()
{
Assert.That(summerVehicle.MakeModel, Is.EqualTo(“Nissan 370Z Coupe”));
}

[Test] public void Should_have_a_standard_steering_wheel()
{
Assert.That(summerVehicle.SteeringWheel, Is.EqualTo(“Standard Steering Wheel”));
}

[Test] public void Should_have_all_season_tires()
{
Assert.That(summerVehicle.Tires, Is.EqualTo(“All Season Tires”));
}

[Test] public void Should_have_a_V6_engine()
{
Assert.That(summerVehicle.Engine, Is.EqualTo(“3.7L V6”));
}

[Test] public void Should_have_leather_seats()
{
Assert.That(summerVehicle.Seats, Is.EqualTo(“Leather Bucket Seats”));
}

[Test] public void Should_have_a_premium_sound_system()
{
Assert.That(summerVehicle.Amenities.Contains(“Premium Sound System”));
}

[Test] public void Should_not_have_any_other_amenities()
{
Assert.That(summerVehicle.Amenities.Count, Is.EqualTo(1));
}
}
}[/source]

Ruby Example

Now let’s create the same vehicle class in Ruby. Really impressed with how much smaller this class is!

[source language=’ruby’] class Vehicle
attr_accessor :steering_wheel, :tires, :seats
attr_accessor :amenities, :engine, :make_model

def initialize
@amenities = [] end
end[/source]

Next, we’ll create our base vehicle factory class. It’s worth noting that Ruby doesn’t have an “abstract” keyword. To get the same effect, we’ll raise an exception if an expected method was not overridden in a subclass.

[source language=’ruby’] require ‘lib/Vehicle’

class VehicleFactoryBase
def create_vehicle
@vehicle_under_construction = Vehicle.new
add_steering_wheel
add_tires
add_seats
add_amenities
add_engine
add_make_model
@vehicle_under_construction
end

protected

def add_steering_wheel
@vehicle_under_construction.steering_wheel = “Standard Steering Wheel”
end

def add_tires
@vehicle_under_construction.tires = “All Season Tires”
end

def add_seats
@vehicle_under_construction.seats = “Cloth Bucket Seats”
end

def add_amenities
end

def add_engine
raise “subclass must include the logic for setting the engine”
end

def add_make_model
raise “subclass must include the logic for setting the make and model”
end
end[/source]

And here’s our first subclass, the winter vehicle factory.

[source language=’ruby’] require ‘lib/Vehicle’
require ‘lib/VehicleFactoryBase’

class WinterVehicleFactory < VehicleFactoryBase def add_tires @vehicle_under_construction.tires = "Snow Tires" end def add_amenities @vehicle_under_construction.amenities.push "4 Wheel Drive" @vehicle_under_construction.amenities.push "Snow Plow" end def add_engine @vehicle_under_construction.engine = "4.7L V-8" end def add_make_model @vehicle_under_construction.make_model = "Jeep Grand Cherokee" end end [/source]

Next up is the summer vehicle factory.

[source language=’ruby’] require ‘lib/Vehicle’
require ‘lib/VehicleFactoryBase’

class SummerVehicleFactory < VehicleFactoryBase def add_seats @vehicle_under_construction.seats = "Leather Bucket Seats" end def add_amenities @vehicle_under_construction.amenities.push "Premium Sound System" end def add_engine @vehicle_under_construction.engine = "3.7L V6" end def add_make_model @vehicle_under_construction.make_model = "Nissan 370Z Coupe" end end [/source]

And of course our unit tests.

[source language=’ruby’] require ‘rubygems’
require “spec”
require ‘lib/WinterVehicleFactory’

describe “A newly created vehicle” do
before(:each) do
@winter_vehicle = WinterVehicleFactory.new.create_vehicle
end

it “should be a Jeep Grand Cherokee” do
@winter_vehicle.make_model.should == “Jeep Grand Cherokee”
end

it “should have a standard steering wheel” do
@winter_vehicle.steering_wheel.should == “Standard Steering Wheel”
end

it “should have snow tires” do
@winter_vehicle.tires.should == “Snow Tires”
end

it “should have a V8 engine” do
@winter_vehicle.engine.should == “4.7L V-8”
end

it “should have standard cloth seats” do
@winter_vehicle.seats.should == “Cloth Bucket Seats”
end

it “should have 4 wheel drive” do
@winter_vehicle.amenities.should include(“4 Wheel Drive”)
end

it “should have a snow plow” do
@winter_vehicle.amenities.should include(“Snow Plow”)
end

it “should not have any other amenities” do
@winter_vehicle.amenities.length.should == 2
end
end[/source]

[source language=’ruby’] require ‘rubygems’
require “spec”
require ‘lib/SummerVehicleFactory’

describe “When creating a summer cruiser” do
before(:each) do
@summer_cruiser = SummerVehicleFactory.new.create_vehicle
end

it “should be a Nissan 370Z” do
@summer_cruiser.make_model.should == “Nissan 370Z Coupe”
end

it “should have a standard steering wheel” do
@summer_cruiser.steering_wheel.should == “Standard Steering Wheel”
end

it “should have all season tires” do
@summer_cruiser.tires.should == “All Season Tires”
end

it “should have a V6 engine” do
@summer_cruiser.engine.should == “3.7L V6”
end

it “should have leather seats” do
@summer_cruiser.seats.should == “Leather Bucket Seats”
end

it “should have a premium sound system” do
@summer_cruiser.amenities.should include(“Premium Sound System”)
end

it “should not have any other amenities” do
@summer_cruiser.amenities.length.should == 1
end
end[/source]

In the next post we’ll look at the strategy pattern. Stay tuned!
kick it on DotNetKicks.com