Template Method Pattern
Category
Behavioral Design Pattern
Overview
The Template Method Pattern defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps of the algorithm without changing its overall structure. This pattern promotes code reuse by enabling a common algorithm structure while permitting variations in specific parts of the algorithm.
This pattern is particularly useful when:
A sequence of steps is fixed but certain steps need customization.
You want to enforce a standard procedure while allowing flexibility for some steps.
Key Characteristics
Algorithm Skeleton:
Defines the structure of an algorithm in a base class.
Allows customization of specific steps by subclasses.
Hooks and Template Methods:
Template Method: Provides the overall structure of the algorithm.
Hooks: Optional methods that allow subclasses to hook into the algorithm for additional customization.
Encapsulation of Invariant Behavior:
Common steps are implemented in the base class to avoid duplication and ensure consistency.
Polymorphism:
Subclasses override specific steps to implement their variant of the algorithm.
Promotes the Hollywood Principle:
The Hollywood Principle is summarized as: “Don’t call us; we’ll call you.” It emphasizes inversion of control, where a higher-level component (e.g., a base class) orchestrates the flow of execution and delegates specific behavior to lower-level components (e.g., subclasses or concrete implementations).
In the Template Method Pattern, this principle is evident because:
The abstract class defines the algorithm’s structure (the “call you” part).
Subclasses override specific steps (the “don’t call us” part) required for the algorithm without dictating the overall process.
The base class controls the execution flow, ensuring that the algorithm remains consistent while allowing flexibility in implementation details.
This principle helps maintain a clear separation of concerns and ensures that higher-level components stay in control of the system’s behavior.
UML Diagram
The UML diagram below illustrates the Template Method Pattern, showing how the base class defines the algorithm structure and subclasses override specific steps:
Implementation Walkthrough
Participants
AbstractClass:
Defines the skeleton of the algorithm using a template method.
Implements common steps of the algorithm and declares abstract or optional methods for customization.
ConcreteClass:
Implements the specific steps of the algorithm by overriding methods in the abstract class.
Example: Preparing a Beverage
Imagine you are creating a system to prepare beverages. The overall process is the same (boil water, brew, pour, add condiments), but specific steps like brewing and adding condiments vary depending on the beverage.
Abstract Class: Beverage
/**
* @brief Abstract class that defines the template method for preparing a beverage.
*/
public abstract class Beverage {
// Template Method
public final void prepareBeverage() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
private void boilWater() {
System.out.println("Boiling water...");
}
private void pourInCup() {
System.out.println("Pouring into cup...");
}
protected abstract void brew();
protected abstract void addCondiments();
// Hook method
protected boolean customerWantsCondiments() {
return true; // Default behavior; subclasses can override
}
}
Concrete Class: Tea
/**
* @brief Concrete implementation of the Beverage for Tea.
*/
public class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("Steeping the tea...");
}
@Override
protected void addCondiments() {
System.out.println("Adding lemon...");
}
}
Concrete Class: Coffee
/**
* @brief Concrete implementation of the Beverage for Coffee.
*/
public class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("Dripping coffee through filter...");
}
@Override
protected void addCondiments() {
System.out.println("Adding sugar and milk...");
}
@Override
protected boolean customerWantsCondiments() {
return false; // No condiments for coffee in this example
}
}
Client Code
/**
* @brief Demonstrates the Template Method Pattern with different beverages.
*/
public class TemplateMethodDemo {
public static void main(String[] args) {
Beverage tea = new Tea();
tea.prepareBeverage();
Beverage coffee = new Coffee();
coffee.prepareBeverage();
}
}
Output
Boiling water...
Steeping the tea...
Pouring into cup...
Adding lemon...
Boiling water...
Dripping coffee through filter...
Pouring into cup...
Applications
When to Use the Template Method Pattern
When multiple subclasses share a common algorithm structure but differ in specific steps.
When you want to enforce a standard procedure while allowing limited flexibility.
When avoiding code duplication for common steps is important.
Common Use Cases
Frameworks and Libraries:
Frameworks often use template methods to provide a common structure while allowing application-specific customizations.
Report Generation:
Generating reports with a common layout but different data sources.
Game Development:
Defining a game loop with hooks for game-specific logic.
Advantages and Disadvantages
Advantages
Code Reuse:
Avoids duplication by implementing common steps in a base class.
Flexibility:
Allows variations in behavior by overriding specific steps.
Consistency:
Ensures that the algorithm follows a consistent structure across implementations.
Encapsulation:
Encapsulates invariant behavior in the base class.
Disadvantages
Increased Complexity:
Requires careful design of abstract and hook methods.
Limited Flexibility:
Subclasses must conform to the predefined structure, reducing freedom.
Inheritance Dependency:
Relies on inheritance, which may lead to issues in complex hierarchies.
Key Takeaways
The Template Method Pattern provides a powerful mechanism to define a fixed algorithm structure while enabling flexibility in specific steps.
By encapsulating invariant behavior and promoting code reuse, it ensures consistency and maintainability across implementations.
Careful design is required to balance the flexibility of hooks with the constraints of the algorithm skeleton.