The Template Method design pattern defines an algorithm in an abstract class, and defers some steps to its subclasses. A Strategy defines a family of pluggable implementations. We’ll see how to convert from Template Method to Strategy.
Template Method
A Template Method has a method that defines an algorithm, using (abstract) methods that subclasses will provide concrete implementations for. The Template Method may have other kinds of methods too, such as helpful operations or hook methods that may optionally be overridden.
Strategy
A Strategy is designed to provide “plug-in” algorithms. A classic example is a paragraph formatter: rather than a complicated method that can do left, right, center, … formatting, make each a strategy and just plug in the right one.
Strategy is typically used when the algorithms have some complexity, though sometimes the “strategy” is mere configuration.
Notice that I allow for passing in data to the strategy. In the original structure, it might have inherited data. Since it’s no longer a subclass, it may need a way to get access to that data. (In some cases, you could set up that access in a constructor instead of the step.)
From Template Method to Strategy
We’re going to evolve from the first approach to the second. It’s not a given this is the right thing to do; you need to assess whether it will work better or communicate more clearly.
Here’s what the end result will look like. ParentTemplate has become StrategyUser. Child1 and Child2 have become Strategy1 and Strategy2 (with a new interface).
To make this transition safe, we may have to do some setup work, but it’s not hard to do.
Example: Yacht Combinations
I recently implemented the Yacht dice game. Part of that game is scoring combinations: you roll 5 dice (with optional re-rolls), and you try to form poker-like combinations such as full house, straight, or four of a kind.
I started with independent classes, but found that they had a similar shape:
if !thisCombinationApplies() { return 0 } return score()
It’s more accurate to say: some combinations had this structure, others could be forced into it.
This made it reasonable to form a Template Method, which could own the (tiny) algorithm above. It was definitely an improvement.
But looking at it, I was slightly disturbed: each combination inherited the Template Method, but this diffused its responsibility. By changing the combination classes to a Strategy, I could let them be focused on what was specific: the algorithm for deciding if the rolls qualified, and the algorithm for scoring when they did.
Before – Template Method
public class Category : Identifiable{ : public func select(_ rolls: [Int]) { score = qualifies(rolls) ? score(rolls) : 0 open = false } func qualifies(_ rolls: [Int]) -> Bool { true } func score(_ rolls: [Int]) -> Int { 0 } } public class Choice : Category { override func score(_ rolls: [Int]) -> Int { rolls.reduce(0, +) } } public class Yacht : Category { override func qualifies(_ rolls: [Int]) -> Bool { return rolls.min() == rolls.max() } override func score(_ rolls: [Int]) -> Int { return 50 } }
Mechanics
My goal in these mechanics is to provide an approach that lets you take steps as tiny as you need to.
- A. Define the SomeStrategy interface, to include all the abstract methods from ParentTemplate. Mark ParentTemplate as implementing SomeStrategy.
B. Build and run tests.
I created ScoreStrategy with two methods qualifies([Int]) and score([Int]).
- A. If the “abstract” methods provide default implementations, move a copy into each child that uses them.
B. Build and run tests
I copied the default qualifies() method (“return true”) down to the Choice class.
- A. If the TemplateMethod defines data that the children access, modify the algorithm to pass the data, and modify the abstract methods to take that data as an argument (rather than accessing it via inheritance). In the worst case, pass the whole TemplateMethod object (for now; you can re-assess that once you get things split up).
B. Build and run tests.
The Category already just passes down the rolls, so no work here.
- A. Change the child methods to use the argument rather than access the parent’s fields.
B. Build and run tests.
Again, didn’t apply since they already used the argument.
- A. Give the TemplateMethod a strategy field and/or method, of type SomeStrategy. Make it so the strategy defaults to “self”.
B. Build and run tests.
I made an optional field strategy and used “defer” in the constructor to set it to self
.
- A. Modify the algorithm to call the abstract methods through the strategy field or method. (“strategy.stepA(data)”)
B. Build and run tests.
My calls looked like strategy.score(rolls).
Temporarily Supporting Both Approaches
- A. Make TemplateMethod take a strategy as an argument to its constructor. (Use the strategy if available; otherwise, default to “self”.)
B. Build and run tests.
I added a second constructor (for now).
- Working one child at a time:
A. Modify each place where a child is constructed: new Childx() should become: new TemplateMethod(new Childx()).
B. Build and run tests.
This work was mostly in the tests. Some tests for the strategy behavior could be simplified (since Category is not needed for some of them).
- Working one child at a time:
A. Make the child implement SomeStrategy, and no longer subclass TemplateMethod.
B. Build and run tests.
Easy; I also removed the extra constructor argument the strategy subclasses had since the Category knows the name. (Alternatively, if there were too many call sites, I could add a temporary constructor.)
Support the Strategy Approach Only
- A. Make the strategy a required argument. (The compiler will warn you if anywhere omitted it.) You can remove any mechanism around strategy being “self”.
B. Make TemplateMethod no longer be abstract and no longer implement the strategy. Delete the protocol method definitions from TemplateMethod.
C. Build and run tests.
I dropped “!” from the strategy field since now it’s always initialized directly. I also deleted methods supporting the protocol.
- A. Assess where data and methods are located: Would they be better in TemplateMethod or the children classes?
B. Assess the names of the classes and methods: Are they still appropriate?
I was happy with where it ended up.
After – Strategy
public class Category : Identifiable{ : private var strategy: ScoreStrategy init(_ aName: String, _ aStrategy: ScoreStrategy) { name = aName strategy = aStrategy } public func select(_ rolls: [Int]) { score = strategy.qualifies(rolls) ? strategy.score(rolls) : 0 open = false } } protocol ScoreStrategy { func qualifies(_ rolls: [Int]) -> Bool func score(_ rolls: [Int]) -> Int } public class Choice : ScoreStrategy { func qualifies(_ rolls: [Int]) -> Bool { true } func score(_ rolls: [Int]) -> Int { rolls.reduce(0, +) } } public class Yacht : ScoreStrategy { func qualifies(_ rolls: [Int]) -> Bool { return rolls.min() == rolls.max() } func score(_ rolls: [Int]) -> Int { return 50 } }
Small Steps
This particular refactoring may be useful on occasion, but the process of developing its description was what really reminded me of the importance of small steps.
That is why I added the notion of the strategy being “self” or a particular strategy instance. That let me support both separate Strategy objects and subclasses of TemplateMethod simultaneously.
I originally had updated the constructor calls after converting to SomeStrategy, but I realized you could do them in smaller steps – and even have a mix of inheritance and delegation – if you updated the calls first.
I also originally didn’t change one class at a time, but a little tuning made it possible to do so. (Since there could be an arbitrary number of strategy classes, and an arbitrary number of calls, I want to make it possible to do small steps there too.)
All this as an example of the “Expand-Flip-Contract” approach that some refactorings use: expand to support both old and new methods, flip things so the old one uses the new one, then contract to eliminate the old approach.
Developing refactorings like this is an iterative process: I refactor while coding (e.g., on Twitch) – and it’s usually a bit sloppy. Then I write up what I did, realizing I can simplify some steps or do something to allow smaller steps. Then I try it, update and repeat. It’s like doing a kata where you call your shot before each step.
Related Work
This refactoring is related to “Replace Subclass with Delegate” in Martin Fowler’s second edition of Refactoring. The differences are due to me specifically targeting Template Method and Strategy, and using an interface as the target.
(I’ll confess that I didn’t check 2/e until after I’d written this article; 1/e contains the related “Replace Inheritance with Delegation” that he now calls “Replace Superclass with Delegate.” This is the downside of having a paper copy of 1/e and an electronic copy of 2/e – the paper is easier to reach for, but “incomplete”:)
References
Design Patterns: Elements of Reusable Object-Oriented Software, by Gamma et al.
Refactoring: Improving the Design of Existing Code 2/e, by Martin Fowler et al.