Defining an algorithm's fixed skeleton in a base class while deferring individual steps to subclasses — via a read-validate-transform-save DataProcessor.
Published September 23, 2026
Some algorithms have a sequence of steps that should always happen in the same order, but where individual steps vary by context. Template Method puts the fixed sequence in a base class method (the "template"), marked final so subclasses can't reorder it, while individual steps are abstract (or have a default, overridable implementation) for subclasses to customize.
abstract class DataProcessor {
// the template — fixed sequence, cannot be reordered or skipped by subclasses
public final void process() {
List<String> raw = read();
List<String> valid = validate(raw);
List<String> transformed = transform(valid);
save(transformed);
}
protected abstract List<String> read();
protected abstract List<String> validate(List<String> raw);
protected abstract List<String> transform(List<String> valid);
protected abstract void save(List<String> data);
}
class CsvDataProcessor extends DataProcessor {
protected List<String> read() { return readCsvFile(); }
protected List<String> validate(List<String> raw) { return raw.stream().filter(this::isValidRow).toList(); }
protected List<String> transform(List<String> valid) { return valid.stream().map(this::normalize).toList(); }
protected void save(List<String> data) { writeToDatabase(data); }
// helper methods omitted
}
class JsonDataProcessor extends DataProcessor {
protected List<String> read() { return readJsonFile(); }
protected List<String> validate(List<String> raw) { return raw.stream().filter(this::isValidJson).toList(); }
protected List<String> transform(List<String> valid) { return valid.stream().map(this::flatten).toList(); }
protected void save(List<String> data) { writeToBlobStorage(data); }
}
DataProcessor csv = new CsvDataProcessor();
csv.process(); // always read -> validate -> transform -> save, in that exact order, guaranteed
Every subclass gets the same guaranteed sequence — you can't accidentally call save() before validate() from within a subclass, because subclasses never call process()'s steps directly; they only ever implement individual steps that the (unoverridable, final) template calls in the fixed order.
Both let you vary behavior, but through opposite mechanisms: Template Method customizes individual steps of an algorithm via inheritance (a subclass overrides specific abstract methods, but the overall structure lives in the base class and can't be swapped as a whole). Strategy swaps the entire algorithm via composition (a completely different PricingStrategy object can be substituted, with no shared base-class structure constraining it at all). Template Method fits when the steps' order must stay fixed and only how each step works varies; Strategy fits when the whole algorithm might be replaced wholesale, with no assumption that it shares any structure with the alternative.
Q: Why mark the template method final?
A: To enforce the entire point of the pattern — if subclasses could override process() itself, they could reorder or skip steps, which defeats the guarantee that the sequence is fixed. final makes that guarantee a compile-time fact, not a convention subclasses are trusted to follow.
Q: Can a step have a default implementation instead of being purely abstract? A: Yes — a "hook" method with a default (often no-op) implementation lets subclasses optionally override just the steps they care about, while inheriting sensible defaults for the rest. This is common when most steps are usually the same across subclasses and only one or two genuinely vary.
Q: Isn't this just normal inheritance and method overriding — what makes it a distinct 'pattern'?
A: The pattern-worthy part isn't overriding itself, it's the specific structure: a final (or otherwise protected-from-override) method that calls several abstract/hook methods in a fixed sequence. That structural commitment — 'the algorithm's shape is fixed, only its steps vary' — is what distinguishes Template Method from arbitrary polymorphism.
Q: How would you test a subclass's individual step in isolation, without running the whole template sequence?
A: Since each step is its own protected method, you can test it directly if you expose it at package-visibility or via a test subclass — though testing through process() end-to-end is usually more valuable, since the steps are specifically meant to be evaluated as part of the fixed sequence, not independently.