Behavioral · Gang of Four
Template Method
Template Method is a behavioral pattern that fixes the outline of an algorithm in a base class and lets subclasses fill in the varying steps. The shared sequence lives in one place.
Example: every import job parses a file, validates rows, then persists. CSV and JSON differ in parse; vendors differ in validate. If you copy the outline into two classes, adding “audit log after persist” means editing both. Put the outline in the base class once instead.
Recipe
Shared outline, hollow steps
The base method is usually so nobody rearranges the outline. The are abstract or have do-nothing defaults. That is inheritance used for a stable sequence, not for a taxonomy of the real world. See composition vs inheritance.
base · final
run(Path file)
calls the three steps in this order. Hollywood: we call you.
- 1
parse(file)
abstractCsvImport / JsonImport
- 2
validate(rows)
abstractVendorA / VendorB
- 3
persist(rows)
sharedImportJob default
Strategy is the composition twin
If the whole policy changes — pricing, allocation — inject a strategy. If the steps stay in order and two of them change, a template is honest. Interviewers often prefer Strategy because you can test the context with a fake and you avoid a deep base class.
Chain is a different shape
A chain of handlers can stop the walk. A template usually runs every step. Factory Method is often a hook inside a template (“which parser?”). The principle is the same sentence.
Java: share the steps, vary the hooks
Keep the base small. That is also OCP: a new vendor is a new subclass, not a new if in run().
abstract class ImportJob {
final void run(Path file) {
List<Row> rows = parse(file);
validate(rows);
persist(rows);
}
abstract List<Row> parse(Path file);
abstract void validate(List<Row> rows);
void persist(List<Row> rows) {
rows.forEach(repository::save);
}
}
class CsvImport extends ImportJob {
List<Row> parse(Path file) { /* split commas */ return List.of(); }
void validate(List<Row> rows) { /* required columns */ }
}
class JsonImport extends ImportJob {
List<Row> parse(Path file) { /* read array */ return List.of(); }
void validate(List<Row> rows) { /* schema */ }
}How to implement it (steps)
Write the outline once
parse → validate → persist. Mark run() final.
Make the varying steps hooks
abstract parse, abstract validate, default persist if shared.
Do not let subclasses reorder
A different outline needs a different template or a Strategy.
A base class that is really a God object
Using inheritance when you meant Strategy
Strategy vs Template, side by side
Same variation, two shapes. Prefer Strategy when you want a fake in tests and no base class. Prefer Template when copying the outline would be the bug and the sequence is the product.
| Lens | Template Method | Strategy |
|---|---|---|
| What is fixed? | The order of steps | The context’s job; the algorithm swaps |
| How you vary | Subclass hooks | Inject an object |
| Test seam | Subclass or extract hooks | Pass a fake strategy |
| Interview default | Import jobs, report pipelines | Pricing, allocation, win rules |
Java: a hook that is a factory method
“Which parser?” can be a factory method inside the template. The outline still owns run().
abstract class ImportJob {
final void run(Path file) {
Parser parser = parser();
List<Row> rows = parser.parse(file);
validate(rows);
persist(rows);
}
abstract Parser parser();
abstract void validate(List<Row> rows);
}Empty overrides are a smell
If JsonImport no-ops validate and persist, you forced the wrong skeleton. Split templates or use Strategy for the steps that actually move.
Keep the base small
JDBC, email, and CSV formatting do not belong on ImportJob. Inject a repository. The template is the sequence.
How to explain this in an interview
“The outline is shared; parse and validate vary. run() is final.” If they then change pricing on a lot, do not subclass ParkingLot — inject a strategy. Empty overrides mean you picked the wrong skeleton. JDBC and email do not belong on ImportJob.
Hollywood: the base calls the hooks. A factory method inside the template is a legal hook, not a second outline.
Nearby in LLD