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