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

Filed Under (.Net, Ruby) by John Miller on 24-04-2009

Tagged Under : , , ,

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.


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<string> Amenities { get; set; }

        public Vehicle()
        {
            Amenities = new List<string>();
        }
    }
}

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.


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;
        }
    }
}

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


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";
        }
    }
}

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


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";
        }
    }
}

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


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));
        }
    }
}

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));
        }
    }
}

Ruby Example

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


class Vehicle
  attr_accessor :steering_wheel, :tires, :seats
  attr_accessor :amenities, :engine, :make_model

  def initialize
    @amenities = []
  end
end

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.


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

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


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

Next up is the summer vehicle factory.


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

And of course our unit tests.


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

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

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

Share/Save/Bookmark

Exploring the Ruby Koans: Building an Object Proxy

Filed Under (Events, Ruby) by John Miller on 14-04-2009

Tagged Under :

Recently I started working through EdgeCase’s Ruby Koans project as a way to become familiar with the Ruby language. The Koans are a set of unit tests that you need to make pass in order to move on to the next step. In making them pass, you learn about the syntax of the Ruby language as well as some of the commonly used libraries.

To get started, follow the instructions on the Koan’s project page on github. When working through them, keep in mind that it’s not a race. Take your time to really understand what each exercise is meant to teach you.

If you’d like a little help getting started with the Koans, one of the author’s, Jim Weirich, will be helping folks at the next Cleveland Ruby user group meeting. Jim is a leader in the Ruby community (active contributor to both Rake and RubyGems) as well as a phenomenal teacher. If you have the evening free, I’d really recommend coming to hear him talk! And in addition to the awesome upcoming CleRb session with Jim Weirich, one of the local .Net SIGs will be featuring Michael Letterle later in the month who will be speaking on IronRuby. Looking forward to both of these events!

In one of the more challenging exercises, you’re asked to build an object proxy that you can wrap around another object to intercept and track method calls made to the underlying object. Below is the solution I came up with (you may want to quit reading if you plan on doing the exercises yourself!). I’m not sure it’s the most elegant solution but it did get the tests passing!


class Proxy
  def initialize(target_object)
    @object = target_object
    @message_log = {}
  end

  def method_missing(method_name, *args, &amp;amp;amp;amp;block)
    if @object.respond_to?(method_name)
      @message_log[method_name] = number_of_times_called(method_name) + 1
      @object.send(method_name, *args, &amp;amp;amp;amp;block)
    else
      super(method_name, *args, &amp;amp;amp;amp;block)
    end
  end

  def messages
    @message_log.keys.reverse
  end

  def called?(method_name)
    @message_log.key?(method_name)
  end

  def number_of_times_called(method_name)
    @message_log[method_name] || 0
  end
end
DotNetKicks Image

Share/Save/Bookmark

Creating Functionality Focused Mockups with Balsamiq

Filed Under (Tools) by John Miller on 12-04-2009

Tagged Under : ,

I’ve never been good at creating mockups for new projects. I’ve tried using tools like Visio or even pencil and paper in the past and always ended up turning to Html. The trouble with this though, as Kevin Berridge points out in a recent post, is that when you present your customers with something that looks too much like an actual application they get lost in the look and feel of what your presenting them instead of functionality. You also run the risk of spending a lot of time and effort on something that will almost certainly be changed many times, or even thrown out completely. Ideally, you want something that allows you to create mockups quickly and keeps the customer’s focus on functionality and not the look of the application. I recently started using Balsamiq Mockups for UI modeling and found that it does this job very, very well. It’s a modeling tool that allows you drag application shapes onto a canvas, much like Visio. The shapes, however, look like handwritten sketches so it helps keeps the users focus on the functionality of what your showing them. The interface is also incredibly simple to use and makes it easy to put together a visual representation of your ideas in minutes.

Below is an example of what you can create using Balsamiq. They’ve also got several stock examples on their website.

Search

The software comes packed with dozens of shapes that range from simple links to Ajaxy sliders to grids with column headers and checkbox columns. Many of the shapes allow you to set values such as font, color, etc to customize their appearance. You can also “group” a set of controls so you can move them around the canvas as one unit.

Your can also add shapes and images of your own when you need something that isn’t included in the library. You can even apply “sketching” to an image to give it the same look as the other shapes.

Wishlist

It would be nice if Balsamiq allowed the concept of templates. For large projects where you would need to create several screens, you would need to create a template, export it as an image, and then use that image as the backdrop to child pages. Not a terrible solution, but not as ideal as being able to simply create a template.

The library contains a lot of controls that sit in a ribbon at the top of the canvas. To add a new control you have to scroll horizontally to the shape you want to add, and drag it to your canvas. You can alternatively start typing the name of the shape in the “quick add” textbox and it will find the shape for you. I would actually like to see a toggle button that would show all the shapes in a larger area (perhaps a modal window).

The product is reasonably priced at $79.00. And they have an awesome demo area on their site that allows you to try the features of the tool so you can determine if it will do what you need before shelling out any money.

In conclusion, I was really impressed with what this tool can do in very little time. Can’t wait to see what they add to it down the road!

DotNetKicks Image

Share/Save/Bookmark

50% off Coupon for JetBrain’s RubyMine

Filed Under (Ruby) by John Miller on 07-04-2009

Tagged Under : ,

JetBrain’s is offering a 50% off coupon for their new Ruby editor, RubyMine. But you have to sign up for the coupon now and they’ll email you a coupon code that you can use when the product is released. Worth signing up for if you’re even the tiniest bit interested in purchasing the release version. They announced that the IDE will run $99 once they go live.

I’ve been running a beta version for a few weeks now and am becoming a big fan. Having key mappings that match Resharper really make the transition back and forth from .Net to Ruby a little less painful.

If you’re interested in following the tool as it progresses, you may want to follow rubymine on twitter.

DotNetKicks Image

Share/Save/Bookmark

Setting Up a Complete Rails Development Environment on Ubuntu

Filed Under (Ruby) by John Miller on 01-04-2009

Tagged Under : , , , ,

I recently finished rebuilding an Ubuntu virtual machine specifically for poking around with Ruby on Rails and it turned out to be quite a learning experience for a guy who has spent the majority of his developer lifetime in windows based systems.

I decided to go with an Ubuntu virtual machine due to some of the stories I’ve heard from others who’ve tried to do Rails development on windows. Initially, I built an Ubuntu 8.10 VM using VMWare Workstation 6.0.x on Vista…but ran into some odd issues with VMWare Tools. After reading the support documentation on VMWare’s website, I found that Ubuntu 8.10 is only supported on VMWare Fusion (yes, the simple solution here would be to join the cool kids and go MBP). The next most recent release, Ubuntu 8.04, is however supported by VMWare Workstation 6.5, so I upgraded Workstation and installed 8.0.4…this time with VMWare Tools playing nicely.

After having to start from scratch a couple of times, I decided to document my installation steps. Hopefully this will help some of you get started…or, more likely, serve as a reminder for myself the next time I need to rebuild.

Step 1: Let’s make sure our system is up to date and have the build software installed.

sudo apt-get update
sudo apt-get dist-upgrade
sudo apt-get install build-essential

Step 2: Install Ruby (I’m using v1.8) and the supporting libraries (rdoc, mysql, etc.).

sudo apt-get install ruby ri rdoc mysql-server libmysql-ruby ruby1.8-dev irb1.8 libdbd-mysql-perl libdbi-perl libmysql-ruby1.8 libmysqlclient15off libnet-daemon-perl libplrpc-perl libreadline-ruby1.8 libruby1.8 mysql-client-5.0 mysql-common mysql-server-5.0 rdoc1.8 ri1.8 ruby1.8 irb libopenssl-ruby libopenssl-ruby1.8 psmisc

Step 3: Install Ruby Gems (I’m on 1.3.1, you may want to check Ruby Forge for the latest version).

wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar xzvf rubygems-1.3.1.tgz
cd rubygems-1.3.1
sudo ruby setup.rb

Step 4: Sanity check – let’s make sure gems are up to date.

sudo gem update –system

Step 5: Just in case a symlink didn’t get automatically created, let’s manually create them.

sudo ln -s /usr/bin/gem1.8 /usr/local/bin/gem
sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb

Step 6: And now it’s time for rails!

sudo gem install rails

Step 7: For testing, you’ll probably want RSpec. (Note that RSpec will be automatically installed as well because it is a dependency of RSpec-Rails).

sudo gem install rspec-rails

Step 8: Git seems to be the preferred choice for source control among rails developers.

sudo apt-get install git-core curl gitweb

After installing, you’ll probably want to set your username/email that other developers will see when you make your commits.

git config –global user.name “your name”
git config –global user.email “youremail@yourdomain.com

Verify the changes
git config –global –list

And if you’re using github for hosting, you’ll need to provide an SSH key. This page has a great overview of how to do that.

Step 9: Sqlite is a great alternative to MySql for local development.

sudo apt-get install sqlite3 swig libsqlite3-ruby libsqlite3-dev
sudo gem install sqlite3-ruby

Step 10: I decided to give JetBrain’s RubyMine a shot. So far, it’s been making the transition from Visual Studio+Resharper a little easier thanks to the common keyboard mappings.

First, you’ll need the Java JDK if you don’t already have it installed.

sudo apt-get install sun-java6-jdk

Next, download RubyMine. After you download it, unpack it to your installation folder of choice.

tar xzvf rubymine749.tar.gz /usr/bin/rubymine749
Update: I believe the above line should have been two steps:
tar xzvf rubymine749.tar.gz
sudo mv rubymine749 /usr/bin/

To run ruby mine, you need to set the JDK_HOME environment variable. I added the line “export JDK_HOME=”/usr/lib/jvm/java-6-sun/” to the top of the file “/usr/bin/rubymine749/bin/rubymine.sh” so I wouldn’t have to remember to set it every time I start the IDE.

To start, run this command from within the /usr/bin/rubymine749/bin folder:
sudo ./rubymine.sh

Step 11: Once I get the hang of the language, I’m going to give Vim another shot as a text editor. Baby steps though, just moving from the my Visual Studio+Resharper sweet spot is enough of shock for now.

sudo apt-get install vim-full vim-gnome vim-ruby vim-rails

And for some awesome Vim extensions, check out what Joe Fiorini has out on github.

Good luck!!

DotNetKicks Image

Share/Save/Bookmark