๐ What is the Template Method Pattern?
The Template Method Pattern is one of the Behavioral Patterns that defines the skeleton of an algorithm in a superclass and allows subclasses to implement specific steps without changing the algorithm's structure.
The core idea of this pattern is to keep the algorithm's structure unchanged while allowing subclasses to redefine certain steps of the algorithm.
๐๏ธ Pattern Structure
The Template Method Pattern consists of the following structure:
- AbstractClass: Defines the template method and abstract methods
- ConcreteClass: Implements the abstract methods concretely
๐จ Advantages and Disadvantages
โ Advantages
- Improved Code Reusability: Common algorithm structures can be reused
- Consistency Guarantee: The overall flow of the algorithm remains consistent
- Easy Maintenance: When common logic needs to be changed, only the superclass needs to be modified
โ Disadvantages
- Inheritance Dependency: Heavily dependent on inheritance relationships
- Limited Flexibility: Difficult to change the algorithm's structure
๐ Java Example: Bus Operation System
Let's implement a bus operation process using the Template Method Pattern.
// Abstract class: Define the template for bus operations
public abstract class Bus {
// Template method: Define the overall flow of bus operations
public final void operate() {
startEngine();
checkSafety();
announceRoute();
driveRoute();
collectFare();
stopEngine();
}
// Common methods
private void startEngine() {
System.out.println("๐ฅ Starting the engine.");
}
private void stopEngine() {
System.out.println("๐ Stopping the engine.");
}
// Abstract methods - implemented by subclasses
protected abstract void checkSafety();
protected abstract void announceRoute();
protected abstract void driveRoute();
protected abstract void collectFare();
}
// City bus implementation
public class CityBus extends Bus {
@Override
protected void checkSafety() {
System.out.println("๐ City bus safety check: Door operation, bell function verification");
}
@Override
protected void announceRoute() {
System.out.println("๐ข Next stop is City Hall Station.");
}
@Override
protected void driveRoute() {
System.out.println("๐ Operating on city roads.");
}
@Override
protected void collectFare() {
System.out.println("๐ณ Please tap your transit card.");
}
}
// Express bus implementation
public class ExpressBus extends Bus {
@Override
protected void checkSafety() {
System.out.println("๐ Express bus safety check: Seat belts, emergency exits verification");
}
@Override
protected void announceRoute() {
System.out.println("๐ข This is the Seoul→Busan express bus.");
}
@Override
protected void driveRoute() {
System.out.println("๐ฃ๏ธ Operating on highways.");
}
@Override
protected void collectFare() {
System.out.println("๐ซ Please show your ticket.");
}
}
// Usage example
public class BusExample {
public static void main(String[] args) {
System.out.println("=== City Bus Operation ===");
Bus cityBus = new CityBus();
cityBus.operate();
System.out.println("\n=== Express Bus Operation ===");
Bus expressBus = new ExpressBus();
expressBus.operate();
}
}
โฐ TypeScript Example: Clock System
Let's implement various types of clocks using the Template Method Pattern.
// Abstract class: Define the template for clocks
abstract class Clock {
// Template method: Define the overall flow of clock operations
public displayTime(): void {
this.updateTime();
this.formatTime();
this.showDisplay();
this.playSound();
}
// Common method
private updateTime(): void {
console.log("โฑ๏ธ Updating current time.");
}
// Abstract methods - implemented by subclasses
protected abstract formatTime(): void;
protected abstract showDisplay(): void;
protected abstract playSound(): void;
}
// Digital clock implementation
class DigitalClock extends Clock {
protected formatTime(): void {
const now = new Date();
const timeString = now.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
console.log(`๐ข Digital format: ${timeString}`);
}
protected showDisplay(): void {
console.log("๐ป Displaying time on LED display.");
}
protected playSound(): void {
console.log("๐ Beep beep beep! Digital alarm sound");
}
}
// Analog clock implementation
class AnalogClock extends Clock {
protected formatTime(): void {
const now = new Date();
const hours = now.getHours() % 12;
const minutes = now.getMinutes();
console.log(`๐ Analog format: ${hours} o'clock ${minutes} minutes`);
}
protected showDisplay(): void {
console.log("โ๏ธ Displaying time with hour and minute hands.");
}
protected playSound(): void {
console.log("๐ Ding ding ding! Clock chimes");
}
}
// Smart watch implementation
class SmartWatch extends Clock {
protected formatTime(): void {
const now = new Date();
const timeString = now.toLocaleString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
console.log(`๐ฑ Smart format: ${timeString}`);
}
protected showDisplay(): void {
console.log("๐บ Displaying time with various information on OLED screen.");
}
protected playSound(): void {
console.log("๐ต Custom notification sound");
}
}
// Usage example
class ClockExample {
static run(): void {
console.log("=== Digital Clock ===");
const digitalClock = new DigitalClock();
digitalClock.displayTime();
console.log("\n=== Analog Clock ===");
const analogClock = new AnalogClock();
analogClock.displayTime();
console.log("\n=== Smart Watch ===");
const smartWatch = new SmartWatch();
smartWatch.displayTime();
}
}
// Execute
ClockExample.run();
๐ฏ Use Case Scenarios
The Template Method Pattern is useful in the following situations:
๐ณ Cooking Recipe System
- When the basic cooking process (preparation→cooking→finishing) is the same, but the specific content of each step differs
๐ Data Processing Pipeline
- When the flow of data reading→processing→saving is the same, but the implementation of each step differs
๐ฎ Game System
- When the basic game flow (initialization→gameplay→result display) is the same, but the specific logic differs for each game
๐ก Conclusion
The Template Method Pattern is a powerful pattern that provides the skeleton of an algorithm while ensuring flexibility in detailed implementation. It reduces code duplication and maintains consistency while allowing each implementation to have its own characteristics.
This pattern is frequently used in framework development and library design, so it's essential to understand it as one of the core concepts of object-oriented design! ๐
๋๊ธ