Template Method
The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It allows one of the algorithm’s steps to be overridden by a subclass without changing the algorithm’s structure. This promotes code reuse and reduces redundancy by centralizing common logic while providing flexibility for specific variations.
This pattern is particularly useful when you have a process with several steps that are largely the same across different scenarios, but certain steps need to be customized. It’s also valuable when you want to enforce a specific order of operations, ensuring consistency while enabling extension. The abstract class implements the overall algorithm, while concrete classes provide implementations for the abstract steps.
Usage
The Template Method pattern is frequently used in scenarios like:
- Framework Development: Creating the basic structure of a framework where the core logic is defined, and clients fill in the specific details.
- Report Generation: Generating reports with a common format, but different sections depending on the type of report.
- Data Processing Pipelines: Implementing a pipeline with stages that are consistent but have varying data transformation logic.
- Game Development: Defining the basic flow of a game level, but allowing different levels to have their specific events or behaviors.
Examples
-
Java I/O Streams: The
InputStreamclass in Java employs the Template Method pattern. Theread()method is the template method, defining the overall process of reading data. Subclasses likeFileInputStreamandByteArrayInputStreamoverride theread()method to provide the specific implementation for reading from a file or a byte array, respectively. The core logic of handling buffering and error checking remains in theInputStreamclass. -
Django’s Class-Based Views (CBVs): Django’s CBVs use this pattern extensively. A base class like
Viewdefines theas_get_view(),as_post_view()methods (the template method) outlining the request handling process. Then, different types of views (e.g.,DetailView,ListView) inherit fromViewand override specific methods likeget()orpost()to perform tailored actions. The overall request-response cycle is managed in the baseViewclass, ensuring consistency across all views. -
Scikit-learn Estimators: In the Python machine learning library scikit-learn, estimators (like
LogisticRegressionorDecisionTreeClassifier) often follow the Template Method pattern. Thefit()method is a template method that defines the learning process. Subclasses implement more specific fitting algorithms while relying on the common infrastructure and validation mechanisms defined in the base estimator class.
Specimens
15 implementationsThe Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The Dart example defines an abstract Game class with a play() method representing the algorithm’s skeleton. initialize(), makeMove(), and endGame() are abstract methods that subclasses must implement, providing the specific steps. Dart’s support for abstract classes and methods makes it a natural fit for this pattern. The concrete Cricket and Football classes inherit from Game and provide their own implementations for the abstract methods, customizing the game logic while maintaining the overall play() sequence.
abstract class Game {
void initialize();
void makeMove();
void endGame();
void play() {
initialize();
makeMove();
endGame();
}
}
class Cricket extends Game {
@override
void initialize() {
print('Cricket Game Initializing: Setting up players and pitch.');
}
@override
void makeMove() {
print('Cricket Game: Batsman bats, Bowler bowls.');
}
@override
void endGame() {
print('Cricket Game Finished: Declaring the winner.');
}
}
class Football extends Game {
@override
void initialize() {
print('Football Game Initializing: Setting up teams and field.');
}
@override
void makeMove() {
print('Football Game: Players dribble and pass the ball.');
}
@override
void endGame() {
print('Football Game Finished: Declaring the winning team.');
}
}
void main() {
var cricketGame = Cricket();
cricketGame.play();
var footballGame = Football();
footballGame.play();
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and reduces redundancy.
The Scala code defines an abstract Game class with a play() method representing the template. play() defines the overall game flow (initialize, run, result) and calls abstract methods like makeMove() and isGameOver() which are implemented by concrete game subclasses (Chess, Monopoly). This leverages Scala’s abstract classes and methods for a clean and type-safe implementation. The use of override is standard Scala practice for subclassing.
// Template Method Pattern in Scala
abstract class Game {
abstract def makeMove(player: String): String
abstract def isGameOver(): Boolean
abstract def getResult(player: String): String
def play(): Unit = {
println("Game Initialized")
var currentPlayer = "Player 1"
while (!isGameOver()) {
println(s"$currentPlayer's turn")
val moveResult = makeMove(currentPlayer)
println(moveResult)
currentPlayer = if (currentPlayer == "Player 1") "Player 2" else "Player 1"
}
println("Game Over")
println(getResult(currentPlayer))
}
}
class Chess extends Game {
override def makeMove(player: String): String = {
s"${player} made a chess move."
}
override def isGameOver(): Boolean = {
// Simplified game over condition for demonstration
scala.util.Random.nextBoolean()
}
override def getResult(player: String): String = {
s"${player} wins the chess game!"
}
}
class Monopoly extends Game {
override def makeMove(player: String): String = {
s"${player} rolled the dice and moved."
}
override def isGameOver(): Boolean = {
// Simplified game over condition for demonstration
scala.util.Random.nextBoolean()
}
override def getResult(player: String): String = {
s"${player} bankrupts everyone and wins Monopoly!"
}
}
object Main {
def main(args: Array[String]): Unit = {
val chessGame = new Chess()
println("Playing Chess:")
chessGame.play()
val monopolyGame = new Monopoly()
println("\nPlaying Monopoly:")
monopolyGame.play()
}
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The PHP example defines an abstract Report class with a displayReport() template method. This method outlines the report generation process (title, data, footer). Subclasses like HtmlReport and PlainTextReport implement the abstract getData() method to provide specific data formatting, customizing the report’s content while maintaining the overall structure defined in the base class. This leverages PHP’s abstract classes and method overriding for a clean and object-oriented implementation.
<?php
abstract class Report {
abstract protected function getData();
public function displayReport() {
$this->displayTitle();
$data = $this->getData();
$this->displayData($data);
$this->displayFooter();
}
protected function displayTitle() {
echo "<h1>Report Title</h1>\n";
}
abstract protected function displayData($data);
protected function displayFooter() {
echo "<p>Copyright 2023</p>\n";
}
}
class HtmlReport extends Report {
protected function getData() {
return "<ul><li>Item 1</li><li>Item 2</li></ul>";
}
protected function displayData($data) {
echo $data . "\n";
}
}
class PlainTextReport extends Report {
protected function getData() {
return "Item 1\nItem 2\n";
}
protected function displayData($data) {
echo $data . "\n";
}
}
// Usage
$htmlReport = new HtmlReport();
$htmlReport->displayReport();
$plainTextReport = new PlainTextReport();
$plainTextReport->displayReport();
?>
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The Ruby code defines an abstract Report class with a template_method that outlines the report generation process. Concrete report types (TextReport, HTMLReport) inherit from Report and override the output_body and output_header methods to customize the report’s content and format, respectively. The template_method in the base class controls the overall flow (header, body, footer), ensuring consistency while allowing for variation in the details. This leverages Ruby’s inheritance and method overriding capabilities for a clean and flexible implementation.
# report.rb
class Report
def template_method
output_header
output_body
output_footer
end
def output_header
raise NotImplementedError, "Subclasses must implement output_header"
end
def output_body
raise NotImplementedError, "Subclasses must implement output_body"
end
def output_footer
puts "Report generated on #{Time.now}"
end
end
class TextReport < Report
def output_header
puts "--- Text Report ---"
end
def output_body
puts "This is the body of the text report."
puts "It contains important information."
end
end
class HTMLReport < Report
def output_header
puts "<html><head><title>HTML Report</title></head><body>"
end
def output_body
puts "<h1>HTML Report</h1>"
puts "<p>This is the body of the HTML report.</p>"
puts "<p>It contains important information.</p>"
end
def output_footer
puts "</body></html>"
end
end
# Example Usage
text_report = TextReport.new
text_report.template_method
html_report = HTMLReport.new
html_report.template_method
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The Swift example defines a CaffeineBeverage class with a prepareBeverage method acting as the template. Subclasses like Coffee and Tea override the brew, addCondiments, and potentially addMilk methods to customize the beverage preparation while maintaining the overall flow defined in the template method. This leverages Swift’s class inheritance and method overriding capabilities for a clean and type-safe implementation, fitting the language’s object-oriented nature.
// CaffeineBeverage.swift
protocol Beverage {
func brew()
func addCondiments()
func addMilk() -> Bool
func prepareBeverage()
}
class CaffeineBeverage: Beverage {
func prepareBeverage() {
brew()
addCondiments()
if addMilk() {
println("Adding milk")
}
}
func brew() {
fatalError("Brew must be overridden by subclasses")
}
func addCondiments() {
fatalError("Add condiments must be overridden by subclasses")
}
func addMilk() -> Bool {
return false
}
}
class Coffee: CaffeineBeverage {
override func brew() {
println("Brewing coffee")
}
override func addCondiments() {
println("Adding sugar and cream to coffee")
}
}
class Tea: CaffeineBeverage {
override func brew() {
println("Brewing tea")
}
override func addCondiments() {
println("Adding lemon to tea")
}
override func addMilk() -> Bool {
return true
}
}
// Example Usage
let coffee = Coffee()
coffee.prepareBeverage()
let tea = Tea()
tea.prepareBeverage()
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The Kotlin example defines an abstract Game class with a play() method representing the algorithm’s skeleton. initialize(), start(), run() and end() are abstract methods that subclasses must implement, providing the specific steps. Concrete game classes like Cricket and Football inherit from Game and provide their own implementations for these steps, while the overall play() sequence remains consistent. Kotlin’s abstract classes and methods naturally support this pattern, and the use of concise function definitions aligns with Kotlin’s style.
abstract class Game {
abstract fun initialize()
abstract fun start()
abstract fun run()
abstract fun end()
fun play() {
println("Game: Starting...")
initialize()
start()
run()
end()
println("Game: Finished.")
}
}
class Cricket : Game() {
override fun initialize() {
println("Cricket: Initializing the game...")
}
override fun start() {
println("Cricket: Starting the match...")
}
override fun run() {
println("Cricket: Playing the innings...")
}
override fun end() {
println("Cricket: Match ended.")
}
}
class Football : Game() {
override fun initialize() {
println("Football: Preparing the field...")
}
override fun start() {
println("Football: Kickoff!")
}
override fun run() {
println("Football: Playing two halves...")
}
override fun end() {
println("Football: Game over.")
}
}
fun main() {
val cricketGame = Cricket()
cricketGame.play()
println("\n--- Next Game ---\n")
val footballGame = Football()
footballGame.play()
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The Rust implementation uses traits to define the abstract algorithm (the template) and requires concrete implementations of the varying steps via methods within the trait. A concrete class then implements the trait, providing the specific logic for the abstract methods. This leverages Rust’s trait system for polymorphism and ensures that all necessary steps are implemented. The use of Self in the trait methods is idiomatic for referring to the implementing type.
// Define the abstract template method
trait Algorithm {
fn step1(&self) -> String;
fn step2(&self) -> String;
fn step3(&self) -> String;
// The template method itself - defines the algorithm's structure
fn run(&self) -> String {
let result = self.step1();
println!("{}", result);
let result = self.step2();
println!("{}", result);
let result = self.step3();
println!("{}", result);
"Algorithm completed".to_string()
}
}
// Concrete implementation 1
struct ConcreteAlgorithmA;
impl Algorithm for ConcreteAlgorithmA {
fn step1(&self) -> String {
"ConcreteAlgorithmA - Step 1".to_string()
}
fn step2(&self) -> String {
"ConcreteAlgorithmA - Step 2".to_string()
}
fn step3(&self) -> String {
"ConcreteAlgorithmA - Step 3".to_string()
}
}
// Concrete implementation 2
struct ConcreteAlgorithmB;
impl Algorithm for ConcreteAlgorithmB {
fn step1(&self) -> String {
"ConcreteAlgorithmB - Step 1".to_string()
}
fn step2(&self) -> String {
"ConcreteAlgorithmB - Step 2".to_string()
}
fn step3(&self) -> String {
"ConcreteAlgorithmB - Step 3".to_string()
}
}
fn main() {
let algo_a = ConcreteAlgorithmA;
let algo_b = ConcreteAlgorithmB;
algo_a.run();
algo_b.run();
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. This allows subclasses to redefine certain steps of an algorithm without changing its structure. It’s useful for encapsulating common parts of an algorithm and promoting code reuse.
Here, we define a Worker interface with a DoWork() method. The AbstractWorker struct implements a template method Execute(), outlining the work process with PreWork(), ActualWork(), and PostWork() steps. ConcreteWorkerA and ConcreteWorkerB override ActualWork() to provide specific implementations while keeping the overall Execute() workflow consistent. This leverages Go’s interface-based polymorphism for a clean implementation.
// worker.go
package main
import "fmt"
// Worker defines the interface for different types of work.
type Worker interface {
DoWork()
}
// AbstractWorker provides the template method.
type AbstractWorker struct{}
// Execute defines the overall workflow. This is the template method.
func (w *AbstractWorker) Execute() {
w.PreWork()
w.ActualWork()
w.PostWork()
}
// PreWork performs setup before the actual work.
func (w *AbstractWorker) PreWork() {
fmt.Println("AbstractWorker: Performing pre-work...")
}
// PostWork performs cleanup after the actual work.
func (w *AbstractWorker) PostWork() {
fmt.Println("AbstractWorker: Performing post-work...")
}
// ActualWork is a placeholder for the specific work to be done.
// Subclasses should override this method.
func (w *AbstractWorker) ActualWork() {
fmt.Println("AbstractWorker: Performing default work...")
}
// ConcreteWorkerA implements the ActualWork method for a specific task.
type ConcreteWorkerA struct{}
func (w *ConcreteWorkerA) ActualWork() {
fmt.Println("ConcreteWorkerA: Performing specific work A...")
}
// ConcreteWorkerB implements the ActualWork method for another task.
type ConcreteWorkerB struct{}
func (w *ConcreteWorkerB) ActualWork() {
fmt.Println("ConcreteWorkerB: Performing specific work B...")
}
func main() {
workerA := &ConcreteWorkerA{}
workerB := &ConcreteWorkerB{}
fmt.Println("Executing Worker A:")
workerA.DoWork() // Calls Execute() which calls PreWork, ActualWork, PostWork
fmt.Println("\nExecuting Worker B:")
workerB.DoWork() // Calls Execute() which calls PreWork, ActualWork, PostWork
}
// DoWork is a method to satisfy the Worker interface.
func (w *AbstractWorker) DoWork() {
w.Execute()
}
func (w *ConcreteWorkerA) DoWork() {
w.Execute()
}
func (w *ConcreteWorkerB) DoWork() {
w.Execute()
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure. In this C implementation, AbstractOperation is the abstract base class defining the overall operation with execute() as the template method. Concrete operation classes (ConcreteOperationA, ConcreteOperationB) implement the abstract performOperation() to provide specific behavior. This avoids code duplication while maintaining a consistent workflow across different operations. C’s function pointers facilitate this pattern, allowing the base class to call the subclass’s implementation.
#include <stdio.h>
// Abstract Class
typedef struct AbstractOperation {
void (*performOperation)(struct AbstractOperation* self); // Function pointer
void (*step1)(struct AbstractOperation* self);
void (*step2)(struct AbstractOperation* self);
} AbstractOperation;
void abstract_operation_step1(AbstractOperation* self) {
printf("AbstractOperation: Step 1\n");
}
void abstract_operation_step2(AbstractOperation* self) {
printf("AbstractOperation: Step 2\n");
}
void abstract_operation_execute(AbstractOperation* self) {
self->step1(self);
self->performOperation(self);
self->step2(self);
}
// Concrete Class A
typedef struct ConcreteOperationA : AbstractOperation {
void (*performOperation)(struct AbstractOperation* self);
};
void concrete_operation_a_performOperation(AbstractOperation* self) {
printf("ConcreteOperationA: Performing operation A\n");
}
void concrete_operation_a_init(struct ConcreteOperationA* op) {
op->performOperation = concrete_operation_a_performOperation;
}
// Concrete Class B
typedef struct ConcreteOperationB : AbstractOperation {
void (*performOperation)(struct AbstractOperation* self);
};
void concrete_operation_b_performOperation(AbstractOperation* self) {
printf("ConcreteOperationB: Performing operation B\n");
}
void concrete_operation_b_init(struct ConcreteOperationB* op) {
op->performOperation = concrete_operation_b_performOperation;
}
int main() {
struct ConcreteOperationA opA;
opA.step1 = abstract_operation_step1;
opA.step2 = abstract_operation_step2;
concrete_operation_a_init(&opA);
printf("Executing Concrete Operation A:\n");
abstract_operation_execute(&opA);
printf("\n");
struct ConcreteOperationB opB;
opB.step1 = abstract_operation_step1;
opB.step2 = abstract_operation_step2;
concrete_operation_b_init(&opB);
printf("Executing Concrete Operation B:\n");
abstract_operation_execute(&opB);
return 0;
}
The Template Method pattern defines the skeleton of an algorithm in an abstract class, but lets subclasses redefine certain steps of the algorithm without changing its structure. It’s a behavioral pattern used for code reuse and to avoid redundant code.
The C++ code below implements the Template Method pattern for building a house. The HouseBuilder abstract class defines the overall process of building a house (buildHouse()) as a template method. Concrete builders like WoodenHouseBuilder and ConcreteHouseBuilder override the specific steps (buildWalls(), buildRoof(), etc.) to create different types of houses, while the core sequence defined in buildHouse() remains consistent. This aligns with C++’s OOP principles, utilizing abstract classes and polymorphism.
#include <iostream>
#include <string>
// Abstract class
class HouseBuilder {
public:
virtual ~HouseBuilder() {}
void buildHouse() {
buildWalls();
buildRoof();
installWindows();
installDoors();
paintHouse();
}
protected:
virtual void buildWalls() = 0;
virtual void buildRoof() = 0;
virtual void installWindows() = 0;
virtual void installDoors() = 0;
virtual void paintHouse() = 0;
};
// Concrete class 1
class WoodenHouseBuilder : public HouseBuilder {
protected:
void buildWalls() override {
std::cout << "Building wooden walls" << std::endl;
}
void buildRoof() override {
std::cout << "Building a wooden roof" << std::endl;
}
void installWindows() override {
std::cout << "Installing wooden windows" << std::endl;
}
void installDoors() override {
std::cout << "Installing wooden doors" << std::endl;
}
void paintHouse() override {
std::cout << "Painting the wooden house" << std::endl;
}
};
// Concrete class 2
class ConcreteHouseBuilder : public HouseBuilder {
protected:
void buildWalls() override {
std::cout << "Building concrete walls" << std::endl;
}
void buildRoof() override {
std::cout << "Building a concrete roof" << std::endl;
}
void installWindows() override {
std::cout << "Installing concrete windows" << std::endl;
}
void installDoors() override {
std::cout << "Installing concrete doors" << std::endl;
}
void paintHouse() override {
std::cout << "Painting the concrete house" << std::endl;
}
};
int main() {
WoodenHouseBuilder woodenBuilder;
ConcreteHouseBuilder concreteBuilder;
woodenBuilder.buildHouse();
std::cout << std::endl;
concreteBuilder.buildHouse();
return 0;
}
The Template Method pattern defines the skeleton of an algorithm in an abstract class, deferring some steps to concrete subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The C# code implements this using an abstract base class AbstractWorkflow with a TemplateMethod defining the overall workflow. Concrete workflow classes like DownloadAndProcessWorkflow and UploadAndProcessWorkflow extend AbstractWorkflow and override the specific ‘primitive operations’ (e.g., Download, Upload, Process) to provide their unique implementations, while maintaining the consistent order defined in the template method. This leverages C#’s inheritance and polymorphism features for a clean and type-safe implementation.
// TemplateMethod.cs
using System;
// Abstract class defining the template method
public abstract class AbstractWorkflow
{
public abstract void Download();
public abstract void Process();
public abstract void Upload();
// Template method defines the algorithm's structure
public void TemplateMethod()
{
Console.WriteLine("Starting Workflow...");
Download();
Process();
Upload();
Console.WriteLine("Workflow Complete.");
}
}
// Concrete workflow 1
public class DownloadAndProcessWorkflow : AbstractWorkflow
{
public override void Download()
{
Console.WriteLine("Downloading data...");
}
public override void Process()
{
Console.WriteLine("Processing downloaded data...");
}
public override void Upload()
{
Console.WriteLine("Upload not required for this workflow.");
}
}
// Concrete workflow 2
public class UploadAndProcessWorkflow : AbstractWorkflow
{
public override void Download()
{
Console.WriteLine("Download not required for this workflow.");
}
public override void Process()
{
Console.WriteLine("Processing uploaded data...");
}
public override void Upload()
{
Console.WriteLine("Uploading data...");
}
}
public class Program
{
public static void Main(string[] args)
{
AbstractWorkflow workflow1 = new DownloadAndProcessWorkflow();
workflow1.TemplateMethod();
Console.WriteLine("\n");
AbstractWorkflow workflow2 = new UploadAndProcessWorkflow();
workflow2.TemplateMethod();
}
}
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows one of the algorithm’s steps to be overridden without changing the algorithm’s structure. This example uses an abstract CoffeeMaker class with a makeCoffee() template method. Concrete classes (SimpleCoffee, Espresso) implement the varying steps like brew() and addIngredients(), while the makeCoffee() method maintains the overall order (boil water, brew, add ingredients, pour). TypeScript’s abstract classes and method overriding capabilities are leveraged for a natural and type-safe implementation.
// Template Method Pattern in TypeScript
// Abstract class defining the template method
abstract class CoffeeMaker {
abstract brew(): void;
abstract addIngredients(): void;
public makeCoffee(): void {
this.boilWater();
this.brew();
this.addIngredients();
this.pour();
}
private boilWater(): void {
console.log("Boiling water...");
}
private pour(): void {
console.log("Pouring coffee...");
}
}
// Concrete class: Simple Coffee
class SimpleCoffee extends CoffeeMaker {
brew(): void {
console.log("Brewing simple coffee...");
}
addIngredients(): void {
console.log("Adding sugar and milk...");
}
}
// Concrete class: Espresso
class Espresso extends CoffeeMaker {
brew(): void {
console.log("Brewing espresso...");
}
addIngredients(): void {
console.log("Adding hot water...");
}
}
// Usage
const simpleCoffeeMaker = new SimpleCoffee();
simpleCoffeeMaker.makeCoffee();
const espressoMaker = new Espresso();
espressoMaker.makeCoffee();
The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.
The JavaScript example defines an abstract CaffeineBeverage class with a prepareBeverage method that outlines the beverage preparation process. Concrete classes like Coffee and Tea inherit from CaffeineBeverage and override the specific steps (e.g., brew, addCondiments) while maintaining the overall preparation sequence. JavaScript’s prototypal inheritance and function overriding capabilities make this pattern a natural fit, leveraging the language’s flexibility without requiring explicit interface definitions.
// CaffeineBeverage.js
class CaffeineBeverage {
prepareBeverage() {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
return "Enjoy your " + this.getName();
}
boilWater() {
console.log("Boiling water...");
}
pourInCup() {
console.log("Pouring into cup...");
}
addCondiments() {
console.log("Adding condiments...");
}
getName() {
return "beverage";
}
}
// Coffee.js
class Coffee extends CaffeineBeverage {
brew() {
console.log("Brewing coffee...");
}
addCondiments() {
console.log("Adding sugar and milk...");
}
getName() {
return "coffee";
}
}
// Tea.js
class Tea extends CaffeineBeverage {
brew() {
console.log("Steeping tea...");
}
addCondiments() {
console.log("Adding lemon...");
}
getName() {
return "tea";
}
}
// App.js
const coffee = new Coffee();
const tea = new Tea();
console.log(coffee.prepareBeverage());
console.log(tea.prepareBeverage());
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids code duplication.
The Python example defines an abstract base class AbstractWorkflow with a template_method that outlines the workflow. Concrete workflows (ConcreteWorkflowA, ConcreteWorkflowB) inherit from this and implement the abstract step1 and step2 methods, customizing specific parts of the workflow while maintaining the overall structure defined in the base class. This leverages Python’s duck typing and ABCs for a clean and flexible implementation.
from abc import ABC, abstractmethod
class AbstractWorkflow(ABC):
def template_method(self):
self.step1()
self.step2()
self.step3()
def step3(self):
print("Step 3: Common logic")
@abstractmethod
def step1(self):
pass
@abstractmethod
def step2(self):
pass
class ConcreteWorkflowA(AbstractWorkflow):
def step1(self):
print("ConcreteWorkflowA: Step 1")
def step2(self):
print("ConcreteWorkflowA: Step 2")
class ConcreteWorkflowB(AbstractWorkflow):
def step1(self):
print("ConcreteWorkflowB: Step 1 - Different implementation")
def step2(self):
print("ConcreteWorkflowB: Step 2 - Different implementation")
if __name__ == "__main__":
workflow_a = ConcreteWorkflowA()
workflow_a.template_method()
workflow_b = ConcreteWorkflowB()
workflow_b.template_method()
The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This is achieved through abstract methods in the base class that are implemented by subclasses. The example demonstrates a Game base class with a template method play(), which defines the game flow. Subclasses like Cricket and Football implement specific steps like initialize(), startGame(), and endGame(), tailoring the game to their rules, while the overall play() structure remains consistent. This leverages Java’s inheritance to provide a clean and extensible solution.
// Template Method Pattern
// Abstract class defining the template
abstract class Game {
// Template method - defines the algorithm's structure
public void play() {
initialize();
startGame();
while (notEnd()) {
takeTurn();
}
endGame();
}
// Concrete methods (common steps)
public void takeTurn() {
System.out.println("Taking a turn...");
}
public boolean notEnd() {
return true; //Placeholder, subclasses should define end condition
}
// Abstract methods (to be implemented by subclasses)
protected abstract void initialize();
protected abstract void startGame();
protected abstract void endGame();
}
// Concrete class - Cricket
class Cricket extends Game {
@Override
protected void initialize() {
System.out.println("Cricket: Initializing the game with bats and balls.");
}
@Override
protected void startGame() {
System.out.println("Cricket: Starting the cricket match.");
}
@Override
protected void endGame() {
System.out.println("Cricket: Ending the cricket match. Declaring the winner.");
}
@Override
public boolean notEnd() {
return Math.random() < 0.9; //Simulate game ending randomly
}
}
// Concrete class - Football
class Football extends Game {
@Override
protected void initialize() {
System.out.println("Football: Initializing the game with players and field.");
}
@Override
protected void startGame() {
System.out.println("Football: Starting the football match.");
}
@Override
protected void endGame() {
System.out.println("Football: Ending the football match. Declaring the winner.");
}
}
// Main class to demonstrate
public class TemplateMethodDemo {
public static void main(String[] args) {
Game cricketGame = new Cricket();
cricketGame.play();
System.out.println("---");
Game footballGame = new Football();
footballGame.play();
}
}