The Template Method pattern defines an algorithm’s sequence in a base class and lets subclasses implement selected steps. The client invokes one operation. That operation calls shared steps and subclass-specific steps in the order defined by the base class.
A subclass changes the implementation of an extension step without duplicating the surrounding algorithm. This works when related operations share a stable sequence. If each subclass needs a different sequence, inheritance may no longer be the right way to share the behavior.
1. How the Template Method Pattern Works
The template method contains the sequence of calls. A required extension step is declared abstract, so every concrete subclass must provide an implementation. An optional extension step has a default implementation; this is commonly called a hook.
For example, a base operation can validate its input, invoke a required calculation, apply an optional adjustment and return the result. The subclass supplies the calculation, while the inherited operation decides when to call it.
These roles describe the relationship:
| Participant | Responsibility |
|---|---|
| Abstract class | Defines the template method, shared work and extension steps. |
| Concrete subclass | Implements required steps and optionally overrides hooks. |
| Client | Selects a concrete subclass and calls its inherited template method. |
Declaring the template method final prevents a subclass from replacing its sequence. The extension methods remain overridable. The pattern can be implemented without final, but then the base class does not enforce that restriction. Java specifies this behavior in its method modifier rules.
2. Template Method Pattern in Action
A parcel checkout needs to calculate shipping quotes for standard and express delivery. Both calculations must reject invalid shipment weights, calculate a base charge, add any surcharge and round the final amount. The rate differs by shipping option, and express delivery adds an extra charge during a peak period.
The example uses whole kilograms and illustrative currency units. It does not contact a carrier or implement a real shipping tariff. The rates are deliberately small enough to check by hand:
| Shipping option | Base charge | Peak surcharge |
|---|---|---|
| Standard | 3.00 plus 1.50 per kilogram | None |
| Express | 6.00 plus 2.50 per kilogram | 2.00 |
ShippingQuote owns the fixed workflow. StandardShipping and ExpressShipping supply the base-charge calculation. Only ExpressShipping overrides the surcharge hook. TemplateMethodDemo acts as the client.
The client makes the same call for either option. The containing class imports java.math.BigDecimal:
ShippingQuote shipping = new ExpressShipping();
BigDecimal amount = shipping.quote(new Shipment(2, true));
System.out.println(amount); // 13.00
This call first validates the shipment. Express delivery then calculates 6.00 + 2 × 2.50 = 11.00, and its hook adds 2.00 for the peak period. The inherited method returns 13.00. The caller does not repeat or assemble those steps.
3. Implementing the Shipping Workflow
The complete source uses JDK 17 or later and standard Java APIs. Each public type below belongs in its own file. Package declarations are included in the repository and omitted from the excerpts.
3.1. Representing a Shipment
The request carries the weight and whether the shipment falls in a peak period:
public record Shipment(int weightKg, boolean peakPeriod) {}
A Java record supplies accessors named after its components. Here, weightKg() returns the weight and peakPeriod() returns the flag. This data carrier deliberately leaves weight validation to the template method so every quote follows the same validation path.
3.2. Defining the Template Method
The base class performs validation itself and declares the calculations that subclasses may vary:
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
public abstract class ShippingQuote {
public final BigDecimal quote(Shipment shipment) {
Objects.requireNonNull(shipment, "shipment is required");
if (shipment.weightKg() <= 0) {
throw new IllegalArgumentException("weight must be positive");
}
BigDecimal base = baseCharge(shipment.weightKg());
BigDecimal extra = surcharge(shipment);
return base.add(extra).setScale(2, RoundingMode.HALF_UP);
}
protected abstract BigDecimal baseCharge(int weightKg);
protected BigDecimal surcharge(Shipment shipment) {
return BigDecimal.ZERO;
}
}
The public quote() operation is the template method. Its calls run in a fixed order: validate the request, calculate the base charge, obtain the surcharge, add the amounts and round.
baseCharge() is required because it has no base implementation. surcharge() is optional because the base class returns zero. A subclass with no additional charge inherits that result without writing an empty override.
The protected methods are extension points for subclasses. The final public method keeps validation and rounding in the shared workflow. A subclass must return non-null amounts from both calculations; the template does not silently replace an invalid result with zero.
3.3. Implementing Standard Shipping
Standard shipping supplies only its base rate:
import java.math.BigDecimal;
public final class StandardShipping extends ShippingQuote {
@Override
protected BigDecimal baseCharge(int weightKg) {
return new BigDecimal("3.00")
.add(new BigDecimal("1.50").multiply(BigDecimal.valueOf(weightKg)));
}
}
For a two-kilogram shipment, the base charge is 3.00 + 2 × 1.50 = 6.00. The inherited hook contributes zero, even during a peak period. The template adds those amounts and returns 6.00.
The decimal rates are constructed from strings so they represent the intended decimal values. BigDecimal performs the arithmetic, and the template explicitly uses RoundingMode.HALF_UP at two decimal places. An application should choose the rounding policy required for its own amounts.
3.4. Implementing Express Shipping and Its Hook
Express shipping supplies a different rate and overrides the optional surcharge:
import java.math.BigDecimal;
public final class ExpressShipping extends ShippingQuote {
@Override
protected BigDecimal baseCharge(int weightKg) {
return new BigDecimal("6.00")
.add(new BigDecimal("2.50").multiply(BigDecimal.valueOf(weightKg)));
}
@Override
protected BigDecimal surcharge(Shipment shipment) {
return shipment.peakPeriod() ? new BigDecimal("2.00") : BigDecimal.ZERO;
}
}
The inherited quote() method still performs the same operations in the same order. When it calls baseCharge(), Java invokes the express implementation. Its later call to surcharge() also invokes the express override, which examines the shipment’s peak-period flag.
Neither subclass implements quote() or repeats its validation. Adding another shipping type requires a base-charge implementation and, only when needed, a surcharge override. Changing the shared order remains a base-class change that can affect every shipping type.
4. Running Normal and Invalid Shipments
From the cloned repository root, compile and run the example:
javac -d out src/main/java/com/howtodoinjava/patterns/behavioral/templatemethod/*.java
java -cp out com.howtodoinjava.patterns.behavioral.templatemethod.TemplateMethodDemo
The demo first compares regular shipments, then changes only the peak-period flag. Finally, it submits a zero-weight shipment:
public class TemplateMethodDemo {
public static void main(String[] args) {
ShippingQuote standard = new StandardShipping();
ShippingQuote express = new ExpressShipping();
Shipment regular = new Shipment(2, false);
Shipment peak = new Shipment(2, true);
System.out.println("Standard: " + standard.quote(regular));
System.out.println("Express: " + express.quote(regular));
System.out.println("Standard at peak: " + standard.quote(peak));
System.out.println("Express at peak: " + express.quote(peak));
try {
express.quote(new Shipment(0, false));
} catch (IllegalArgumentException e) {
System.out.println("Invalid shipment: " + e.getMessage());
}
}
}
The output is:
Standard: 6.00
Express: 11.00
Standard at peak: 6.00
Express at peak: 13.00
Invalid shipment: weight must be positive
The standard quote stays at 6.00 because that subclass inherits the zero surcharge. The express quote increases from 11.00 to 13.00 because its hook responds to the changed flag. Both results pass through the same rounding operation.
Invalid weight is rejected before either subclass calculation runs. The exception exits quote() and is handled by the demo. A later calculation can also throw; without an appropriate exception handler, the remaining steps will not run. A fixed order is not a guarantee that every step succeeds.
5. Choosing Between Template Method and Strategy
Template Method is useful when related classes share a stable workflow and differ at a few defined steps. The base class determines which operations subclasses can customize. This makes common checks easy to keep together, but it also couples subclasses to the base class’s extension contract.
Strategy moves variable behavior into a separate collaborating instance. The distinction matters when a checkout needs to change behavior independently of its class hierarchy:
| Requirement | Suitable direction |
|---|---|
| Related types follow the same sequence with a few different steps | Template Method. |
| Select or replace an algorithm independently of the containing class | Strategy. |
| Combine many independently selectable rates, surcharges and rules | Composition often avoids a subclass for every combination. |
A template can also delegate one step to a strategy. These patterns can work together, provided that the division of responsibilities remains clear. Avoid adding hooks for hypothetical variations; each hook becomes part of the contract that subclasses depend on.
6. Frequently Asked Questions
The main distinction is between the fixed operation and its extension points. These questions help decide which behavior belongs in each place.
6.1. Must the Base Class Be abstract?
Not necessarily. A base class can provide defaults for every step and still define a template method. This example declares ShippingQuote abstract because baseCharge() has no useful default. Every concrete shipping option must supply that calculation.
6.2. Can a Hook Return a Value?
Yes. A hook is an overridable operation with a default implementation; it does not have to return void. Here, surcharge() returns a decimal amount, and zero is a meaningful default. The template uses that returned amount in the final calculation.
6.3. Should the Constructor Call the Template Method?
Avoid calling overridable behavior while a base constructor runs. A subclass’s fields may not yet be initialized when its override executes. Construct the instance first, then call the template method through the normal public API. Java’s instance creation rules explain this initialization order.
6.4. Is There a Standard Java Example?
AbstractList is a skeletal implementation of List. Its documented extension contract lets a subclass provide get(int) and size() for an unmodifiable list while inherited operations reuse those primitives. It demonstrates shared operations built on subclass-supplied behavior, even though it is not a shipping-style sequence.
7. Conclusion
Use Template Method when a base class should own a stable algorithm and subclasses should customize specific steps. Keep required steps and optional hooks distinct, define their return contracts, and test failures as well as successful variations. Prefer composition when the variations need independent selection or substantially different workflows.
Happy Learning!
The complete example is available in the GitHub source folder.