Template Method Pattern in Ruby

One of the most ubiquitous patterns in life is the template. It enables us to generalize behavior - thus ignoring or postponing complexity.

This ability to generalize behavior - deferring derivative details - turns out to be extremely useful in agile software development. It encourages mock-object practices. It encourages simplest possible solutions. It encourages test-driven development. And most importantly it embraces change.

What is the template-method pattern? A good example is found in JUnit. TestCase.run() defines a generic process: 1) setup before running test; 2) run test; 3) tear down after running test. The complexity is provided by derivative TestCase types - but the process is same for every run.

public class TestCase {
  // details elided
  public void run() {
    setup();
    runTest();
    tearDown();
  }
  protected void runTest() {
  }
  protected void setUp() {
  }
  protected void tearDown() {
  }
}

Let's walk through definition and implementation of a template method pattern. We will generalize a sorting process and defer the details to derivative types. For those familiar with Ruby - ignore the fact that Array includes a sort method.

First we create a test case to assert creation of our Sorter.

require "test/unit"
require "arraysorter"

module Applanet module Research module Ruby module Patterns
  class ArraySorterTest < Test::Unit::TestCase
    def test_creation
      sorter = Applanet::Research::Ruby::Patterns::ArraySorter.new
    end
  end
end end end end

To get this test to pass we must create a minimal ArraySorter.

module Applanet module Research module Ruby module Patterns
  class ArraySorter
  end
end end end end

Now we are ready to create a test for sorting.

require "test/unit"
require "arraysorter"

module Applanet module Research module Ruby module Patterns
  class ArraySorterTest < Test::Unit::TestCase
    def test_creation
      sorter = Applanet::Research::Ruby::Patterns::ArraySorter.new
    end
    def test_array_sort
      sorter = Applanet::Research::Ruby::Patterns::ArraySorter.new
      first_value = sorter.sort(["x", "y", "z", "a", "b", "c"])[0]
      self.assert_equal(first_value, "a")
    end
  end
end end end end

Which leads us to defining the template method.

module Applanet module Research module Ruby module Patterns
  class Sorter
    private_class_method :new
    def sort(values)
      sort_values(values)
    end
  end
end end end end

To finally get this to pass we must implement the details in sort_values.

require "sorter"

module Applanet module Research module Ruby module Patterns
  class ArraySorter < Sorter
    public_class_method :new
    def sort_values(values)
      values.sort { |x,y| x <=> y }
    end
    protected :sort_values
  end
end end end end