Template Method and Inheritance
In Design Patterns, the Gang of Four describe the Template Method pattern. In this pattern, a base class method describes the structure of an algorithm or process and calls other methods for the steps. The base class can either force subclasses to implement the step methods or it can contain a default implementation that subclasses can choose to override if necessary.
Consider a very simple unit testing framework. We might create
instances of TestCase
subclasses with the name of the test method to
run.
Some subclasses might need to do some setup before running the test,
or teardown afterwards. That’s easy enough to accomplish using
super
:
This works, but is less than ideal. Every subclass that needs setup
or teardown now has to override run
and remember to call super
.
Subclasses that only need setup will have a different variant of
run
than subclasses that only need teardown. If we decide that we
want to do extra work in run
, then every subclass needs to be
examined to make sure it still works with the new base class
implementation.
For example, teardown
should be called even if the test itself fails
or raises an exception. Given the design above, we’d have to go
through every subclass that implements teardown
and change the run
method.
Instead, we can refactor TestCase
to use the Template Method
pattern:
With this version of TestCase
, subclasses no longer have to override
run
. Instead, they just implement whichever of setup
or
teardown
they need. That’s less code to write, less duplication,
and more flexibility for the base class.
Consider the run
method from
Minitest:
By using the Template Method pattern, Minitest has been able to add
more features in its run
method without forcing all subclasses to be
changed to accommodate the new features:
- It handles the
INFO
signal to print out information about which test is currently running. - It times each test so that it can provide timing information about the tests being run.
- It captures exceptions in a uniform way and turns them into test failures.
- It adds more hook methods to the test lifecycle (
:before_setup
,:after_setup
,:before_teardown
, and:after_teardown
)
The Template Method pattern works really well in concert with the Composed Method pattern. Template Methods should generally be written as Composed Methods.
If you are working on a base class and you find that many of your subclasses are going to have to override the same method and call super, consider refactoring to the Template Method pattern instead.