4
Posted September 27, 2011 by Spyros in Design Patterns
 
 

The Strategy Design Pattern in Ruby

najdorf move 6
najdorf move 6

Programming large projects can be a quite tedious projects that needs lots of code refactoring again and again, if the code is not well written from the beginning. A very efficient way to make sure that your code is of the highest standards, is by utilizing design patterns if applicable. A design pattern is actually a way of organizing your code so that it is well written, maintainable and extensible. There are various design patterns fitting lots of different situations. One of my favorites is the Strategy design pattern.

What is the Strategy design pattern ?

In the formal sense, the Strategy pattern is one that defines a family of algorithms, encapsulated each one of them and makes it so that they can be interchanged. In a more informal and more understandable definition, it’s a neat way to dynamically assign behaviour to a particular client object (class object). Let’s think of an example. Illustrate that we are creating a new game where there is a user that has a first class attribute, which can either be a Ranger, a Fighter, or a Magician. Each class has different skills and, say, different cost to research.

Now, this already creates some questions for us to solve. How will we handle the situation ? Let’s see how one could do it :

1. We could start by having a User class. This class has an attribute named first_class that can be either set as one of the three types (Ranger, Fighter, Magician). Thus, we would see this :


user = User.new
user.first_class = 'Ranger'
puts user.cost

Now, you may already see the problem in this. The cost function implemented by the User class has serious design flaws. Let’s see its possible implementation :


def cost
  if self.first_class == 'Ranger'
    100
  elsif self.first_class == 'Fighter'
    200
  elsif self.first_class == 'Magician'
    130
  end
end

An important design principle of good code is the OPEN/CLOSED principle. That code must be open for extension, but closed for modification. Think what will happen if we ever need to add a new first_class, say ‘Knight’. We would need to change the cost function and every other function that operates in the same IF-ELSE way. That would be a big nightmare and a big design problem. Moreover, user.cost does not actually mean anything. Even if we create the method so that it’s something like user.first_class_cost, we are actually passing first class behavior to the User class. Not a good thing, this class is probably doing too much in terms of responsibilities. We should really make sure that every class only does as much as needed and nothing more. So, how can we make this code better and more extensible ?

Enter The Strategy Design Pattern 

Let’s start by seeing how this code changes when transformed to utilize the Strategy Pattern :


class User
  attr_accessor :name, :first_class

  def first_class= klass
    @first_class = klass.new
  end

  def first_class_description
    @first_class_description = @first_class.description
  end

end

class FirstClass

  def vehicle
    'car'
  end

end

class Ranger < FirstClass

  def cost
    30
  end

end

class Fighter < FirstClass

  def cost
    20
  end

end

See the difference now ? The idea is that we still have a User model, but now we act smarter on defining its first_class. Instead of making it a nightmare String based value, we are actually making it a new model. A new User object now expects a new first_class object in order to operate correctly (we can add it to the constructor as well, if needed). Now, in this example, every first_class gets a new vehicle automatically, so that each new first class character gets a ‘car’. Of course, this can be extended as well, using the same pattern, but it just serves as an example in showing how static things can just stay in the main class. Now, a Ranger, Fighter or anything else, inherits FirstClass and defines a new set of algorithms that operate on the base class. Pretty straight forward i guess.

The best thing about this pattern now is that our object does not need to know what it is, in order to present the description or the cost of its class. The behavior is dynamically set. Let’s see the example execution :


user = User.new

user.first_class = Ranger
puts user.first_class.description
puts user.first_class.cost

user.first_class = Fighter
puts user.first_class_description
puts user.first_class.cost

And when we execute the program we get :

A Ranger here
30
A Fighter here
20

As a side note, see that i also define a first_class_description method, which delegates responsibility directly to the first_class model. This is actually what Rails does with the delegate_to method. It’s a good practice and a fully encapsulated way to do things. That’s it with the Strategy Pattern in Ruby, hope that it will help you in designing your implementation.


Spyros