Design Patterns · Behavioral Patterns
Template Method
Keep an algorithm's sequence in a base class. Let subclasses replace selected steps while preserving the workflow's promises.
This lesson follows Strategy. Strategy varies behavior through composition. Template Method varies steps through inheritance.
Template Method defines an algorithm's skeleton in a base-class method. Subclasses provide or override selected steps. The cost is coupling each subclass to that sequence and its extension rules.
Find the repeated sequence
A tool imports labels from plain lines or a JSON array. Decoding differs. Trimming values and rejecting empty labels are shared. Copying all three steps into each importer creates two places to maintain the same validation rule.
abstract class LabelImporter {
run(source: string): string[] {
const labels = this.decode(source).map(label => label.trim());
if (labels.some(label => label.length === 0)) {
throw new Error("Empty label");
}
this.afterLoad(labels.length);
return labels;
}
protected abstract decode(source: string): string[];
protected afterLoad(_count: number): void {}
}
class LineImporter extends LabelImporter {
protected decode(source: string) { return source.split("\n"); }
}
class JsonImporter extends LabelImporter {
protected decode(source: string): string[] {
const value: unknown = JSON.parse(source);
if (!Array.isArray(value) || !value.every(item => typeof item === "string")) {
throw new Error("Expected an array of strings");
}
return value;
}
protected afterLoad(count: number) {
console.log("Imported", count, "labels");
}
}
new LineImporter().run(" alpha\nbeta "); // ["alpha", "beta"]
new JsonImporter().run('[" alpha", "beta "]'); // same labels
Distinguish required steps, defaults, and hooks
| Extension point | Meaning | Here |
|---|---|---|
| Abstract step | every concrete subclass must provide it | decode |
| Default step | base behavior exists and may be replaced | a separately extracted normalize step could use a default |
| Hook | optional extension with an empty default | afterLoad |
Try it
Run the same skeleton with another subclass
Lines decode → [' alpha', 'beta ']
normalize → ['alpha', 'beta']
validate → both nonempty
afterLoad → no-op
JSON decode → [' alpha', 'beta ']
normalize → ['alpha', 'beta']
validate → both nonempty
afterLoad → logs 2 labels
Step through
Trace a rejected label
Decode the format
input: ['alpha', ' ']
Apply shared normalization
labels: ['alpha', '']
Reject before the success hook
Empty label error
afterLoad does not run
A hook's position determines what it may assume. A success hook is different from cleanup in finally.
Preserve the skeleton's guarantees
The language may not protect the template method
Java can mark a method final. TypeScript has no final method modifier. The sample relies on subclasses honoring the contract and leaving run() intact. If extension code cannot be trusted to preserve it, composition can offer a firmer boundary.
Do not add an override point for every line. Every extension point increases the behaviors the base class must support. A subclass that disables required validation violates the base promise even if its methods compile.
Check yourself
How many extension hooks should a template expose?
Correct. Every hook adds another behavior the base algorithm must support.
Not quite. That makes the skeleton difficult to reason about and creates unsupported extension points.
Map the roles
| Role | In this example | Job |
|---|---|---|
| Abstract class | LabelImporter | Declares the methods that act as algorithm steps, plus run(), the template method that calls them in a fixed order. Steps can be abstract or have a default implementation. |
| Concrete classes | LineImporter, JsonImporter | Override the steps they need to. They must not override the template method itself. |
Check yourself
Which method must a concrete importer leave alone?
Not quite. Hooks exist precisely so subclasses can override them.
Correct. Subclasses vary steps. Replacing the skeleton breaks the pattern's promise.
Reach for it when subclasses should vary steps, not the whole algorithm
- you want clients to extend only particular steps of an algorithm, not the whole algorithm or its structure. The template method turns a monolithic algorithm into a series of steps that subclasses can extend while the superclass keeps the structure intact.
- several classes contain almost identical algorithms with small differences. Without the pattern, a change to the algorithm means editing every class. Turning it into a template method lets you pull the similar steps up into a superclass and leave only the differing code in subclasses.
Check yourself
Three importers each copy the same trim-and-validate code and differ only in how they decode. Which Template Method use is this?
Not quite. That is the opposite. The structure stays fixed.
Correct. Pull the shared steps into the base class and keep only decode() in each subclass.
Implement it in five steps
Refactor existing code toward the pattern in this order:
- Analyze the target algorithm to see whether you can break it into steps. Decide which steps are common to all subclasses and which are always unique.
- Create the abstract base class. Declare the template method and a set of abstract methods for the steps. In the template method, call the steps in order to outline the algorithm's structure. Consider marking the template method
finalso subclasses cannot override it. - All steps can be abstract, but some benefit from a default implementation, so subclasses do not have to implement them.
- Think about adding hooks between the crucial steps of the algorithm.
- For each variation of the algorithm, create a concrete subclass. It must implement every abstract step and may override some of the optional ones.
Check yourself
What is a hook, as opposed to an abstract step?
Correct. The algorithm works even if a hook is never overridden.
Not quite. That describes an abstract step.
Name what it costs
| You gain | You pay |
|---|---|
| Clients override only certain parts of a large algorithm, so changes elsewhere affect them less | Some clients may be limited by the skeleton the algorithm provides |
| Duplicate code moves into a superclass | A subclass that suppresses a default step through an override can violate the Liskov substitution principle |
| Optional hooks give extension points around important steps | The more steps a template has, the harder it is to maintain |
Check yourself
A subclass overrides the validation step with an empty body, so empty labels slip through. Which principle does that break?
Not quite. No client is forced to depend on unused methods here.
Correct. Suppressing a step that the base class guarantees changes behavior callers rely on.
Do not confuse it with its neighbors
| Pattern | How it relates |
|---|---|
| Factory Method | Factory Method is a specialization of Template Method. A factory method can also serve as one step in a larger template method. |
| Strategy | Template Method is based on inheritance: you change parts of an algorithm by extending those parts in subclasses. It works at the class level, so it is static. Strategy is based on composition: you change parts of an object's behavior by supplying different strategies. It works at the object level, so you can switch behavior at runtime. |
Check yourself
The same importer object must switch decoding format in the middle of a session. Template Method or Strategy?
Not quite. Changing a step at runtime on the same object needs composition.
Correct. A Template Method variant is fixed once you choose the subclass.
Retrieve and apply
Check yourself
A subclass replaces run() and skips required validation. Does implementing decode() make it a valid template-method extension?
Correct. The extension must preserve the workflow contract. Providing one abstract step does not excuse removing required shared behavior.
Not quite. The pattern is about varying selected steps while preserving the algorithm's structure.
Mark shared steps, required steps, and hooks in one repeated workflow. Continue to Visitor when new operations must cover several stable element types.
Source: Alexander Shvets, Dive Into Design Patterns (深入设计模式), Chinese edition v2021-1.25. Template Method, printed pages 357–368. Explanations, examples, and exercises are adapted for this course.