Community Oriented

Gems

Web Based

Projects For Me

The Template and Strategy patterns

Exploring design patterns, 1 Sep 2011

Yesterday Doug showed me the Template and Strategy design patterns. Here's a quick rundown in Ruby. Note that I'm showing them as close to their static language implementation as possible.

Template pattern

So you need some object that prints "Hello, ____!", but the code that decides which word goes where the blank is needs to be able to change. The Template pattern would have you create a subclass with the customized code:

class Greeter
  def to_s
    "Hello, #{name}!"
  end

  # This method isn't necessary in Ruby, but static languages
  # like Java would require you to declare it as abstract
  def name
    raise 'Should be overridden'
  end
end


class GreetNamed < Greeter
  attr_accessor :name
  def initialize(name)
    self.name = name
  end
end
GreetNamed.new("Joe").to_s # => "Hello, Joe!"

Of course, this allows us to specify many possible implementations. For example, here is a class that will read the name from standard input, and write the greeting to standard output.

class GreetFromStdin < Greeter
  def name
    puts "What's your name?"
    gets.chomp
  end
end
puts GreetFromStdin.new

And because this is Ruby, we can override on the fly (Actually, Java can do this one, too).

greeter = Greeter.new
def greeter.name
  "Amar"
end
greeter.to_s # => "Hello, Amar!"

Strategy pattern

Another pattern, and Doug prefers this as it has looser dependencies, is for the greeter to have an object which implements the method it needs.

class Greeter
  attr_accessor :namer
  def initialize(namer)
    self.namer = namer
  end
  
  def to_s
    "Hello, #{namer.name}!"
  end
end


# This class isn't necessary in Ruby, but it is in statically typed 
# languages. In Java, it would be an interface.
class Namer
  def name
  end
end


# A class that specifies the name on instantiation
class GreetNamed < Namer
  attr_accessor :name
  def initialize(name)
    self.name = name
  end
end
namer = GreetNamed.new "Joe"
Greeter.new(namer).to_s # => "Hello, Joe!"

And because this is Ruby, you don't need to inherit from the Namer, Any object which has a name method will do.

require 'ostruct'
namer = OpenStruct.new :name => "Ayaan"
Greeter.new(namer).to_s # => "Hello, Ayaan!"
blog comments powered by Disqus