01Jul
2009

Tags:

Design Patterns in Ruby

I have a confession to make.

You know that really popular design patterns book that most software developers (especially in the Java world) claim to have read? You know the one I’m talking about. The one written by the “Gang of Four”. The one you were supposed to have read during your junior or senior year of college. Yeah, this one.

I’ve never read it.

Sure, I’ve owned it. I have even thumbed through it a few times. But I’ve never sat down with any real intention of absorbing the information within its covers. The problem for me personally was that when I first opened Design Patterns I was just starting my software development career. I was too young and hadn’t seen enough code to really comprehend the authors’ message or understand the need for such a book.

A few years have passed since then and while it’s still early in my career I now have a much better idea of how to develop software and have a much better understanding of Object Oriented Design. Even though I spend a significant amount of my free time developing my skills and reading about software development I have yet to read the seminal Design Patterns book.

Design patterns have left a bad taste in my mouth. Over the years they have been abused and misunderstood. They seem to have become caught up in the all too familiar silver bullet buzzword vernacular of the IT industry (SOA anyone?). So I simply avoided them. Unless I was about to interview for a new job, design patterns never so much as crossed my mind. Repeat after me: Singleton, Factory, MVC. You’re hired!

But things have changed. Over the past year and a half or so I’ve really started to focus on my career as a software developer. I’ve learned new operating systems and languages, created this blog, and started attending local developer meetings on a regular basis. While I still have my favorite technologies and interests, I’ve been doing a much better job of keeping an open mind about trying out new development tools and methodologies.

Enter Russ Olsen. In addition to being a great developer and all around good guy, Russ is someone who I had the honor of working with at the beginning of my career. He was very much a mentor and I credit him with teaching me many of the applied fundamentals of software development. So what does Russ have to do with design patterns? Well it just so happens that a few years after I left the company where Russ and I worked together he wrote a book called Design Patterns in Ruby. Because of my respect for Russ and my fondness of Ruby I decided I’d give the book a shot. While I was certainly expecting to learn some new things, I was surprised (although I shouldn’t have been) by how much great information I was able to extract from this book.

In their most minimal form, design patterns are simply reusable solutions that have been created to solve common coding problems. While this may seem intuitive, it was actually somewhat of a revelation to me – a person who has ignored the direct usage of design patterns for all these years. I see now that given an appropriate situation design patterns can be used to create consistent, maintainable code. The important thing to remember is that each pattern has a specific use and should only be used when applicable.

Although the title of the book is Design Patterns in Ruby, a majority of the principles and patterns apply to many other object oriented languages as well. In fact, while I was reading Russ’s book I was in the middle of working on a small Java project. This project had to keep track of rapid state changes on a particular website. I needed to keep track of a countdown timer, a list of participants, and an aggregate score. Each of these pieces of information was updated independently, but I needed to make decisions in my code based on the combination of this information at any given time. After reading the chapter on the Observer pattern, I realized it would be the perfect fit. Prior to using the pattern my code was a huge pile of tangled spaghetti. Afterwards, it was clean and easy to read.

I really liked the way the chapters were organized in Design Patterns in Ruby. Russ started each chapter by posing a common programming problem. Then he would explain how a specific design pattern could be used to solve that problem. As the chapter progressed Russ would work through the problem using examples that were very easy to understand. Each chapter ended with real world examples of the patterns as they have been implemented in the Ruby core libraries or various open source projects. I found the real world code samples to be especially useful as compliments to the examples given in the book.

Not only did I enjoy reading this book because it was written by Russ, but I genuinely feel like it has helped to make me a better programmer. In addition, it has helped increase my Ruby specific programming skills. Learning to program in Ruby is easy, but learning to program the “Ruby Way” is a much greater challenge. Ruby is full of powerful idioms that are not necessarily obvious without a deep examination of the language. Russ’s book helps enlighten would be Ruby programmers to many of Ruby’s powerful language features.

For example, compare an implementation of the Proxy pattern written in Java and Ruby:

Java Version

MathService.java

public interface MathService {
    public void add(int x, int y);
    public void subtract(int x, int y);
}

MathServiceImpl.java

public class MathServiceImpl implements MathService {
    public void add(int x, int y) {
        System.out.println("Result ("+x+ " + "+y+"): " + (x + y));
    }

    public void subtract(int x, int y) {
        System.out.println("Result ("+x+ " - "+y+"): " + (x - y));
    }
}

MathServiceProxy.java

public class MathServiceProxy implements MathService {
    private MathService delegate;

    public MathServiceProxy(MathService ms){
        this.delegate = ms;
    }

    public void add(int x, int y) {
        System.out.println("Call made to the add method");
        delegate.add(x, y);
    }

    public void subtract(int x, int y) {
        System.out.println("Call made to the subtract method");
        delegate.subtract(x, y);
    }
}

MathServiceDriver.java

public class MathServiceDriver {
    public static void main(String[] args) {
        MathService ms = new MathServiceProxy(new MathServiceImpl());
        ms.add(1, 2);
        ms.subtract(4, 3);
    }
}

Ruby Version

math_service.rb

class MathService
  def add(x, y)
    puts "Result (#{x} + #{y}): #{x + y}"
  end

  def subtract(x, y)
    puts "Result (#{x} - #{y}): #{x - y}"
  end
end

class MathServiceProxy
  def initialize(math_service)
    @delegate = math_service
  end

  def method_missing(name, *args)
    puts "Call made to the #{name} method"
    @delegate.send(name, *args)
  end
end

ms = MathServiceProxy.new(MathService.new)
ms.add(1,2)
ms.subtract(4,3)

Result of Running Either Program

Call made to the add method
Result (1 + 2): 3
Call made to the subtract method
Result (4 - 3): 1

In this example we use the Proxy pattern to print out a message before each method call. If we decided to add any methods to our MathService interface we would need to update our MathServiceImpl and MathServiceProxy Java classes as well. However, by using Ruby’s built in method_missing method we have the ability to wrap each MathService method call dynamically without having to write a new method in our MathServiceProxy class. Ruby removes the tedium of implementing the Proxy pattern.

This is just one small example of the flexibility and power provided by the Ruby language. Before reading Russ’s book I didn’t necessarily “get” many of Ruby’s idioms. Reading Design Patterns in Ruby has certainly helped solidify my understanding and is great for programmers who would like to take their Ruby skills to the next level.

Code Samples

All code samples from this blog entry can be downloaded from GitHub by clicking here

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • DZone
  • FSDaily
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  1. 3 Responses to “Giving Design Patterns a Second Chance: Ruby Edition”

  2. Awesome review Justin!

    I loved this book as well. I can’t say enough about it really. I did a presentation based on it last year that went over pretty well. http://www.codeofficer.com/blog/entry/my_ruby_design_patterns_talk_of_april_14th/ Thanks for sharing your thoughts!

    By CodeOfficer on Jul 7, 2009

  3. I’m fairly certain the java example could be made somewhat slicker with reflection… but if you’re relaying heavily on that, then you may as well use Ruby.

    By fakeleft on Jul 7, 2009

  4. @fakeleft

    I agree that the Java example could probably be made “slicker” with reflection. I was simply trying to point out that Ruby’s dynamic nature makes it much easier to implement certain patterns as compared to other languages like Java.

    Anyway, thank you very much for the comment!

    By Justin Spradlin on Jul 7, 2009

Post a Comment