Template Method Pattern
Template Method Pattern: Algorithm Skeletons
Section titled “Template Method Pattern: Algorithm Skeletons”Now let’s dive into the Template Method Pattern - one of the most fundamental behavioral design patterns that defines the skeleton of an algorithm in a base class, letting subclasses override specific steps without changing the algorithm’s structure.
Why Template Method Pattern?
Section titled “Why Template Method Pattern?”Imagine you’re making different types of beverages - tea and coffee. The process is similar: boil water, brew, pour, add condiments. But “brew” means steeping tea leaves for tea, and filtering coffee for coffee. The Template Method Pattern lets you define this common process once, with specific steps filled in by subclasses!
The Template Method Pattern defines the skeleton of an algorithm in a base class method, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
What’s the Use of Template Method Pattern?
Section titled “What’s the Use of Template Method Pattern?”The Template Method Pattern is useful when:
- You have a common algorithm structure - Same steps, different details
- You want to avoid code duplication - Common code in base class
- You need to control extension points - Subclasses can only customize certain steps
- You want to enforce algorithm structure - Subclasses can’t change the order
- You have invariant steps - Some steps must always happen
What Happens If We Don’t Use Template Method Pattern?
Section titled “What Happens If We Don’t Use Template Method Pattern?”Without the Template Method Pattern, you might:
- Duplicate code - Same algorithm structure in multiple places
- Inconsistent implementations - Different classes implement differently
- Hard to maintain - Changes needed in multiple places
- No control over extensions - Subclasses can change everything
- Violate DRY - Don’t Repeat Yourself principle violated
Simple Example: The Beverage Maker
Section titled “Simple Example: The Beverage Maker”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Algorithm Flow
Section titled “Algorithm Flow”Here’s how the template method works - same structure, different implementations:
sequenceDiagram
participant Client
participant Tea
participant Coffee
participant BaseClass as Beverage (Base)
Client->>Tea: prepare_recipe()
activate Tea
Tea->>BaseClass: boil_water()
BaseClass-->>Tea: Water boiled
Tea->>Tea: brew() [steep tea]
Note right of Tea: Subclass implementation
Tea->>BaseClass: pour_in_cup()
BaseClass-->>Tea: Poured
Tea->>Tea: add_condiments() [add lemon]
Note right of Tea: Subclass implementation
Tea-->>Client: Tea ready!
deactivate Tea
Client->>Coffee: prepare_recipe()
activate Coffee
Coffee->>BaseClass: boil_water()
BaseClass-->>Coffee: Water boiled
Coffee->>Coffee: brew() [drip coffee]
Note right of Coffee: Different implementation
Coffee->>BaseClass: pour_in_cup()
BaseClass-->>Coffee: Poured
Coffee->>Coffee: add_condiments() [add milk]
Note right of Coffee: Different implementation
Coffee-->>Client: Coffee ready!
deactivate Coffee
The Problem
Section titled “The Problem”You’re building a beverage maker that can make tea and coffee. Without Template Method Pattern:
1# ❌ Without Template Method Pattern - Code duplication!2
3class Tea:4 def prepare(self):5 # Step 1: Boil water6 print("Boiling water...")7
8 # Step 2: Brew (specific to tea)9 print("Steeping the tea...")10
11 # Step 3: Pour12 print("Pouring into cup...")13
14 # Step 4: Add condiments (specific to tea)15 print("Adding lemon...")16
17class Coffee:18 def prepare(self):19 # Step 1: Boil water - DUPLICATED!20 print("Boiling water...")21
22 # Step 2: Brew (specific to coffee)23 print("Dripping coffee through filter...")24
25 # Step 3: Pour - DUPLICATED!26 print("Pouring into cup...")27
28 # Step 4: Add condiments (specific to coffee)29 print("Adding milk and sugar...")30
31class HotChocolate:32 def prepare(self):33 # Step 1: Boil water - DUPLICATED AGAIN!34 print("Boiling water...")35
36 # Step 2: Mix (specific to hot chocolate)37 print("Mixing chocolate powder...")38
39 # Step 3: Pour - DUPLICATED AGAIN!40 print("Pouring into cup...")41
42 # Step 4: Add condiments43 print("Adding marshmallows...")44
45# Problems:46# - boil_water() and pour() duplicated in EVERY class47# - If we change how we boil water, change in EVERY class48# - No enforcement of algorithm structure49# - Easy to forget a step1// ❌ Without Template Method Pattern - Code duplication!2
3class Tea {4 public void prepare() {5 // Step 1: Boil water6 System.out.println("Boiling water...");7
8 // Step 2: Brew (specific to tea)9 System.out.println("Steeping the tea...");10
11 // Step 3: Pour12 System.out.println("Pouring into cup...");13
14 // Step 4: Add condiments (specific to tea)15 System.out.println("Adding lemon...");16 }17}18
19class Coffee {20 public void prepare() {21 // Step 1: Boil water - DUPLICATED!22 System.out.println("Boiling water...");23
24 // Step 2: Brew (specific to coffee)25 System.out.println("Dripping coffee through filter...");26
27 // Step 3: Pour - DUPLICATED!28 System.out.println("Pouring into cup...");29
30 // Step 4: Add condiments (specific to coffee)31 System.out.println("Adding milk and sugar...");32 }33}34
35class HotChocolate {36 public void prepare() {37 // Step 1: Boil water - DUPLICATED AGAIN!38 System.out.println("Boiling water...");39
40 // Step 2: Mix (specific to hot chocolate)41 System.out.println("Mixing chocolate powder...");42
43 // Step 3: Pour - DUPLICATED AGAIN!44 System.out.println("Pouring into cup...");45
46 // Step 4: Add condiments47 System.out.println("Adding marshmallows...");48 }49}50
51// Problems:52// - boilWater() and pour() duplicated in EVERY class53// - If we change how we boil water, change in EVERY class54// - No enforcement of algorithm structure55// - Easy to forget a stepProblems:
- Code duplication - Same steps copied everywhere
- Maintenance nightmare - Change in multiple places
- No structure enforcement - Easy to forget steps
- Violates DRY principle
The Solution: Template Method Pattern
Section titled “The Solution: Template Method Pattern”Class Structure
Section titled “Class Structure”classDiagram
class Beverage {
<<abstract>>
+prepare_recipe() void
+boil_water() void
#brew()* void
+pour_in_cup() void
#add_condiments()* void
}
class Tea {
#brew() void
#add_condiments() void
}
class Coffee {
#brew() void
#add_condiments() void
}
class HotChocolate {
#brew() void
#add_condiments() void
}
Beverage <|-- Tea : extends
Beverage <|-- Coffee : extends
Beverage <|-- HotChocolate : extends
note for Beverage "Template method: prepare_recipe()\nConcrete: boil_water(), pour_in_cup()\nAbstract: brew(), add_condiments()"
1from abc import ABC, abstractmethod2
3# Step 1: Create the Abstract Class with Template Method4class Beverage(ABC):5 """Abstract base class with template method"""6
7 def prepare_recipe(self) -> None:8 """9 Template method - defines the algorithm skeleton.10 This method is final - subclasses cannot override it!11 """12 self.boil_water()13 self.brew() # Abstract - subclasses implement14 self.pour_in_cup()15 self.add_condiments() # Abstract - subclasses implement16 print(f"✅ {self.__class__.__name__} is ready!\n")17
18 def boil_water(self) -> None:19 """Concrete method - same for all beverages"""20 print("🔥 Boiling water...")21
22 def pour_in_cup(self) -> None:23 """Concrete method - same for all beverages"""24 print("☕ Pouring into cup...")25
26 @abstractmethod27 def brew(self) -> None:28 """Abstract method - subclasses must implement"""29 pass30
31 @abstractmethod32 def add_condiments(self) -> None:33 """Abstract method - subclasses must implement"""34 pass35
36# Step 2: Create Concrete Subclasses37class Tea(Beverage):38 """Concrete class - implements abstract methods for tea"""39
40 def brew(self) -> None:41 print("🍵 Steeping the tea for 3 minutes...")42
43 def add_condiments(self) -> None:44 print("🍋 Adding lemon slice...")45
46class Coffee(Beverage):47 """Concrete class - implements abstract methods for coffee"""48
49 def brew(self) -> None:50 print("☕ Dripping coffee through filter...")51
52 def add_condiments(self) -> None:53 print("🥛 Adding milk and sugar...")54
55class HotChocolate(Beverage):56 """Concrete class - implements abstract methods for hot chocolate"""57
58 def brew(self) -> None:59 print("🍫 Mixing chocolate powder with water...")60
61 def add_condiments(self) -> None:62 print("🍡 Adding marshmallows and whipped cream...")63
64# Step 3: Use the pattern65def main():66 print("=" * 50)67 print("Beverage Maker (Template Method Pattern)")68 print("=" * 50)69
70 # Make tea71 print("\n📋 Making Tea:")72 print("-" * 30)73 tea = Tea()74 tea.prepare_recipe()75
76 # Make coffee77 print("📋 Making Coffee:")78 print("-" * 30)79 coffee = Coffee()80 coffee.prepare_recipe()81
82 # Make hot chocolate83 print("📋 Making Hot Chocolate:")84 print("-" * 30)85 hot_chocolate = HotChocolate()86 hot_chocolate.prepare_recipe()87
88 print("=" * 50)89 print("✅ Template Method Pattern: Same algorithm, different details!")90 print("✅ Common code in base class - no duplication!")91
92if __name__ == "__main__":93 main()1// Step 1: Create the Abstract Class with Template Method2abstract class Beverage {3 /**4 * Abstract base class with template method5 */6
7 // Template method - defines the algorithm skeleton8 // This method is final - subclasses cannot override it!9 public final void prepareRecipe() {10 boilWater();11 brew(); // Abstract - subclasses implement12 pourInCup();13 addCondiments(); // Abstract - subclasses implement14 System.out.println("✅ " + getClass().getSimpleName() + " is ready!\n");15 }16
17 // Concrete method - same for all beverages18 private void boilWater() {19 System.out.println("🔥 Boiling water...");20 }21
22 // Concrete method - same for all beverages23 private void pourInCup() {24 System.out.println("☕ Pouring into cup...");25 }26
27 // Abstract method - subclasses must implement28 protected abstract void brew();29
30 // Abstract method - subclasses must implement31 protected abstract void addCondiments();32}33
34// Step 2: Create Concrete Subclasses35class Tea extends Beverage {36 /**37 * Concrete class - implements abstract methods for tea38 */39 @Override40 protected void brew() {41 System.out.println("🍵 Steeping the tea for 3 minutes...");42 }43
44 @Override45 protected void addCondiments() {46 System.out.println("🍋 Adding lemon slice...");47 }48}49
50class Coffee extends Beverage {51 /**52 * Concrete class - implements abstract methods for coffee53 */54 @Override55 protected void brew() {56 System.out.println("☕ Dripping coffee through filter...");57 }58
59 @Override60 protected void addCondiments() {61 System.out.println("🥛 Adding milk and sugar...");62 }63}64
65class HotChocolate extends Beverage {66 /**67 * Concrete class - implements abstract methods for hot chocolate68 */69 @Override70 protected void brew() {71 System.out.println("🍫 Mixing chocolate powder with water...");72 }73
74 @Override75 protected void addCondiments() {76 System.out.println("🍡 Adding marshmallows and whipped cream...");77 }78}79
80// Step 3: Use the pattern81public class Main {82 public static void main(String[] args) {83 System.out.println("=".repeat(50));84 System.out.println("Beverage Maker (Template Method Pattern)");85 System.out.println("=".repeat(50));86
87 // Make tea88 System.out.println("\n📋 Making Tea:");89 System.out.println("-".repeat(30));90 Beverage tea = new Tea();91 tea.prepareRecipe();92
93 // Make coffee94 System.out.println("📋 Making Coffee:");95 System.out.println("-".repeat(30));96 Beverage coffee = new Coffee();97 coffee.prepareRecipe();98
99 // Make hot chocolate100 System.out.println("📋 Making Hot Chocolate:");101 System.out.println("-".repeat(30));102 Beverage hotChocolate = new HotChocolate();103 hotChocolate.prepareRecipe();104
105 System.out.println("=".repeat(50));106 System.out.println("✅ Template Method Pattern: Same algorithm, different details!");107 System.out.println("✅ Common code in base class - no duplication!");108 }109}Real-World Software Example: Data Mining Pipeline
Section titled “Real-World Software Example: Data Mining Pipeline”Now let’s see a realistic software example - a data mining pipeline that processes data from different sources.
The Problem
Section titled “The Problem”You’re building a data processing system that needs to handle CSV files, JSON APIs, and databases. The process is similar: open connection, extract data, transform, analyze, generate report, close connection. Without Template Method:
1# ❌ Without Template Method Pattern - Duplicated process!2
3class CSVDataMiner:4 def mine(self, path: str):5 # Open - specific to CSV6 print(f"Opening CSV file: {path}")7
8 # Extract - specific to CSV9 print("Parsing CSV rows...")10 data = [{"name": "Alice"}, {"name": "Bob"}]11
12 # Transform - COMMON!13 print("Transforming data...")14 transformed = [d["name"].upper() for d in data]15
16 # Analyze - COMMON!17 print("Analyzing data...")18 result = {"count": len(transformed)}19
20 # Generate report - COMMON!21 print(f"Report: {result}")22
23 # Close - specific to CSV24 print("Closing CSV file")25
26class JSONAPIMiner:27 def mine(self, url: str):28 # Open - specific to API29 print(f"Connecting to API: {url}")30
31 # Extract - specific to API32 print("Fetching JSON from API...")33 data = [{"name": "Charlie"}, {"name": "Diana"}]34
35 # Transform - DUPLICATED!36 print("Transforming data...")37 transformed = [d["name"].upper() for d in data]38
39 # Analyze - DUPLICATED!40 print("Analyzing data...")41 result = {"count": len(transformed)}42
43 # Generate report - DUPLICATED!44 print(f"Report: {result}")45
46 # Close - specific to API47 print("Closing API connection")48
49# Problems:50# - transform(), analyze(), generate_report() duplicated51# - If analysis logic changes, update ALL miners52# - No guarantee all miners follow same process1// ❌ Without Template Method Pattern - Duplicated process!2
3class CSVDataMiner {4 public void mine(String path) {5 // Open - specific to CSV6 System.out.println("Opening CSV file: " + path);7
8 // Extract - specific to CSV9 System.out.println("Parsing CSV rows...");10 List<Map<String, String>> data = List.of(11 Map.of("name", "Alice"),12 Map.of("name", "Bob")13 );14
15 // Transform - COMMON!16 System.out.println("Transforming data...");17
18 // Analyze - COMMON!19 System.out.println("Analyzing data...");20
21 // Generate report - COMMON!22 System.out.println("Report generated");23
24 // Close - specific to CSV25 System.out.println("Closing CSV file");26 }27}28
29class JSONAPIMiner {30 public void mine(String url) {31 // Open - specific to API32 System.out.println("Connecting to API: " + url);33
34 // Extract - specific to API35 System.out.println("Fetching JSON from API...");36
37 // Transform - DUPLICATED!38 System.out.println("Transforming data...");39
40 // Analyze - DUPLICATED!41 System.out.println("Analyzing data...");42
43 // Generate report - DUPLICATED!44 System.out.println("Report generated");45
46 // Close - specific to API47 System.out.println("Closing API connection");48 }49}50
51// Problems:52// - transform(), analyze(), generateReport() duplicated53// - If analysis logic changes, update ALL miners54// - No guarantee all miners follow same processProblems:
- Common steps duplicated across all miners
- Changes need to be made in multiple places
- No enforcement of the mining process
The Solution: Template Method Pattern
Section titled “The Solution: Template Method Pattern”Class Structure
Section titled “Class Structure”classDiagram
class DataMiner {
<<abstract>>
+mine(source) void
#open_source(source)* void
#extract_data()* List
+transform_data(data) List
+analyze_data(data) Report
+generate_report(analysis) void
#close_source()* void
+hook_before_analysis() void
}
class CSVDataMiner {
#open_source(source) void
#extract_data() List
#close_source() void
}
class JSONAPIMiner {
#open_source(source) void
#extract_data() List
#close_source() void
}
class DatabaseMiner {
#open_source(source) void
#extract_data() List
#close_source() void
+hook_before_analysis() void
}
DataMiner <|-- CSVDataMiner : extends
DataMiner <|-- JSONAPIMiner : extends
DataMiner <|-- DatabaseMiner : extends
note for DataMiner "Template method: mine()\nAbstract: open, extract, close\nConcrete: transform, analyze, report\nHook: hook_before_analysis()"
1from abc import ABC, abstractmethod2from typing import List, Dict, Any3from dataclasses import dataclass4
5# Step 1: Define data structures6@dataclass7class AnalysisReport:8 """Report generated from data analysis"""9 source_type: str10 record_count: int11 summary: Dict[str, Any]12
13# Step 2: Create the Abstract Class with Template Method14class DataMiner(ABC):15 """Abstract data miner with template method"""16
17 def mine(self, source: str) -> AnalysisReport:18 """19 Template method - defines the data mining algorithm.20 Subclasses cannot change this structure!21 """22 print(f"\n{'='*50}")23 print(f"📊 Starting data mining: {self.__class__.__name__}")24 print(f"{'='*50}")25
26 # Step 1: Open source (abstract - source-specific)27 self.open_source(source)28
29 # Step 2: Extract data (abstract - source-specific)30 raw_data = self.extract_data()31 print(f" 📥 Extracted {len(raw_data)} records")32
33 # Step 3: Transform data (concrete - common for all)34 transformed_data = self.transform_data(raw_data)35
36 # Hook: Optional step before analysis37 self.hook_before_analysis(transformed_data)38
39 # Step 4: Analyze data (concrete - common for all)40 report = self.analyze_data(transformed_data)41
42 # Step 5: Generate report (concrete - common for all)43 self.generate_report(report)44
45 # Step 6: Close source (abstract - source-specific)46 self.close_source()47
48 return report49
50 # Abstract methods - MUST be implemented by subclasses51 @abstractmethod52 def open_source(self, source: str) -> None:53 """Open the data source"""54 pass55
56 @abstractmethod57 def extract_data(self) -> List[Dict[str, Any]]:58 """Extract raw data from source"""59 pass60
61 @abstractmethod62 def close_source(self) -> None:63 """Close the data source"""64 pass65
66 # Concrete methods - Common implementation for all67 def transform_data(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:68 """Transform raw data - common for all miners"""69 print(" 🔄 Transforming data...")70 transformed = []71 for record in data:72 # Normalize keys to lowercase73 transformed.append({k.lower(): v for k, v in record.items()})74 return transformed75
76 def analyze_data(self, data: List[Dict[str, Any]]) -> AnalysisReport:77 """Analyze data - common for all miners"""78 print(" 📈 Analyzing data...")79
80 # Calculate statistics81 summary = {82 "fields": list(data[0].keys()) if data else [],83 "sample": data[:2] if len(data) >= 2 else data84 }85
86 return AnalysisReport(87 source_type=self.__class__.__name__,88 record_count=len(data),89 summary=summary90 )91
92 def generate_report(self, report: AnalysisReport) -> None:93 """Generate report - common for all miners"""94 print(" 📝 Generating report...")95 print(f" Source: {report.source_type}")96 print(f" Records: {report.record_count}")97 print(f" Fields: {report.summary['fields']}")98
99 # Hook method - CAN be overridden, but not required100 def hook_before_analysis(self, data: List[Dict[str, Any]]) -> None:101 """Hook - optional step before analysis. Override if needed."""102 pass103
104# Step 3: Create Concrete Subclasses105class CSVDataMiner(DataMiner):106 """Mines data from CSV files"""107
108 def __init__(self):109 self._file = None110
111 def open_source(self, source: str) -> None:112 print(f" 📂 Opening CSV file: {source}")113 self._file = source # Simulated file handle114
115 def extract_data(self) -> List[Dict[str, Any]]:116 print(" 📋 Parsing CSV rows...")117 # Simulated CSV data118 return [119 {"Name": "Alice", "Age": "30", "City": "NYC"},120 {"Name": "Bob", "Age": "25", "City": "LA"},121 {"Name": "Charlie", "Age": "35", "City": "Chicago"},122 ]123
124 def close_source(self) -> None:125 print(f" 📁 Closing CSV file")126 self._file = None127
128class JSONAPIMiner(DataMiner):129 """Mines data from JSON APIs"""130
131 def __init__(self):132 self._connection = None133
134 def open_source(self, source: str) -> None:135 print(f" 🌐 Connecting to API: {source}")136 self._connection = source # Simulated connection137
138 def extract_data(self) -> List[Dict[str, Any]]:139 print(" 📡 Fetching JSON from API...")140 # Simulated API response141 return [142 {"id": 1, "username": "alice_dev", "active": True},143 {"id": 2, "username": "bob_admin", "active": True},144 {"id": 3, "username": "charlie_user", "active": False},145 ]146
147 def close_source(self) -> None:148 print(f" 🔌 Closing API connection")149 self._connection = None150
151class DatabaseMiner(DataMiner):152 """Mines data from databases"""153
154 def __init__(self):155 self._connection = None156
157 def open_source(self, source: str) -> None:158 print(f" 🗄️ Connecting to database: {source}")159 self._connection = source # Simulated DB connection160
161 def extract_data(self) -> List[Dict[str, Any]]:162 print(" 🔍 Executing SQL query...")163 # Simulated database result164 return [165 {"order_id": 1001, "amount": 99.99, "status": "completed"},166 {"order_id": 1002, "amount": 149.99, "status": "pending"},167 {"order_id": 1003, "amount": 29.99, "status": "completed"},168 {"order_id": 1004, "amount": 199.99, "status": "cancelled"},169 ]170
171 def close_source(self) -> None:172 print(f" 🔒 Closing database connection")173 self._connection = None174
175 # Override hook to add custom behavior176 def hook_before_analysis(self, data: List[Dict[str, Any]]) -> None:177 """Custom hook - validate data before analysis"""178 print(" ✅ Validating database records...")179 invalid = [d for d in data if d.get("status") == "cancelled"]180 if invalid:181 print(f" ⚠️ Found {len(invalid)} cancelled orders")182
183# Step 4: Use the pattern184def main():185 print("=" * 60)186 print("Data Mining Pipeline (Template Method Pattern)")187 print("=" * 60)188
189 # Mine CSV data190 csv_miner = CSVDataMiner()191 csv_miner.mine("users.csv")192
193 # Mine JSON API data194 api_miner = JSONAPIMiner()195 api_miner.mine("https://api.example.com/users")196
197 # Mine Database data (with custom hook)198 db_miner = DatabaseMiner()199 db_miner.mine("postgresql://localhost/orders")200
201 print("\n" + "=" * 60)202 print("✅ Template Method Pattern: Same process, different sources!")203 print("✅ Common logic in base class - no duplication!")204 print("✅ Hooks allow optional customization!")205
206if __name__ == "__main__":207 main()1import java.util.*;2
3// Step 1: Define data structures4class AnalysisReport {5 /**6 * Report generated from data analysis7 */8 private String sourceType;9 private int recordCount;10 private Map<String, Object> summary;11
12 public AnalysisReport(String sourceType, int recordCount, Map<String, Object> summary) {13 this.sourceType = sourceType;14 this.recordCount = recordCount;15 this.summary = summary;16 }17
18 public String getSourceType() { return sourceType; }19 public int getRecordCount() { return recordCount; }20 public Map<String, Object> getSummary() { return summary; }21}22
23// Step 2: Create the Abstract Class with Template Method24abstract class DataMiner {25 /**26 * Abstract data miner with template method27 */28
29 // Template method - defines the data mining algorithm30 // Subclasses cannot change this structure!31 public final AnalysisReport mine(String source) {32 System.out.println("\n" + "=".repeat(50));33 System.out.println("📊 Starting data mining: " + getClass().getSimpleName());34 System.out.println("=".repeat(50));35
36 // Step 1: Open source (abstract - source-specific)37 openSource(source);38
39 // Step 2: Extract data (abstract - source-specific)40 List<Map<String, Object>> rawData = extractData();41 System.out.println(" 📥 Extracted " + rawData.size() + " records");42
43 // Step 3: Transform data (concrete - common for all)44 List<Map<String, Object>> transformedData = transformData(rawData);45
46 // Hook: Optional step before analysis47 hookBeforeAnalysis(transformedData);48
49 // Step 4: Analyze data (concrete - common for all)50 AnalysisReport report = analyzeData(transformedData);51
52 // Step 5: Generate report (concrete - common for all)53 generateReport(report);54
55 // Step 6: Close source (abstract - source-specific)56 closeSource();57
58 return report;59 }60
61 // Abstract methods - MUST be implemented by subclasses62 protected abstract void openSource(String source);63 protected abstract List<Map<String, Object>> extractData();64 protected abstract void closeSource();65
66 // Concrete methods - Common implementation for all67 protected List<Map<String, Object>> transformData(List<Map<String, Object>> data) {68 System.out.println(" 🔄 Transforming data...");69 List<Map<String, Object>> transformed = new ArrayList<>();70 for (Map<String, Object> record : data) {71 Map<String, Object> newRecord = new HashMap<>();72 for (Map.Entry<String, Object> entry : record.entrySet()) {73 newRecord.put(entry.getKey().toLowerCase(), entry.getValue());74 }75 transformed.add(newRecord);76 }77 return transformed;78 }79
80 protected AnalysisReport analyzeData(List<Map<String, Object>> data) {81 System.out.println(" 📈 Analyzing data...");82
83 Map<String, Object> summary = new HashMap<>();84 summary.put("fields", data.isEmpty() ? List.of() : new ArrayList<>(data.get(0).keySet()));85 summary.put("sample", data.size() >= 2 ? data.subList(0, 2) : data);86
87 return new AnalysisReport(88 getClass().getSimpleName(),89 data.size(),90 summary91 );92 }93
94 protected void generateReport(AnalysisReport report) {95 System.out.println(" 📝 Generating report...");96 System.out.println(" Source: " + report.getSourceType());97 System.out.println(" Records: " + report.getRecordCount());98 System.out.println(" Fields: " + report.getSummary().get("fields"));99 }100
101 // Hook method - CAN be overridden, but not required102 protected void hookBeforeAnalysis(List<Map<String, Object>> data) {103 // Default: do nothing. Override if needed.104 }105}106
107// Step 3: Create Concrete Subclasses108class CSVDataMiner extends DataMiner {109 /**110 * Mines data from CSV files111 */112 private String file;113
114 @Override115 protected void openSource(String source) {116 System.out.println(" 📂 Opening CSV file: " + source);117 this.file = source;118 }119
120 @Override121 protected List<Map<String, Object>> extractData() {122 System.out.println(" 📋 Parsing CSV rows...");123 return Arrays.asList(124 Map.of("Name", "Alice", "Age", "30", "City", "NYC"),125 Map.of("Name", "Bob", "Age", "25", "City", "LA"),126 Map.of("Name", "Charlie", "Age", "35", "City", "Chicago")127 );128 }129
130 @Override131 protected void closeSource() {132 System.out.println(" 📁 Closing CSV file");133 this.file = null;134 }135}136
137class JSONAPIMiner extends DataMiner {138 /**139 * Mines data from JSON APIs140 */141 private String connection;142
143 @Override144 protected void openSource(String source) {145 System.out.println(" 🌐 Connecting to API: " + source);146 this.connection = source;147 }148
149 @Override150 protected List<Map<String, Object>> extractData() {151 System.out.println(" 📡 Fetching JSON from API...");152 return Arrays.asList(153 Map.of("id", 1, "username", "alice_dev", "active", true),154 Map.of("id", 2, "username", "bob_admin", "active", true),155 Map.of("id", 3, "username", "charlie_user", "active", false)156 );157 }158
159 @Override160 protected void closeSource() {161 System.out.println(" 🔌 Closing API connection");162 this.connection = null;163 }164}165
166class DatabaseMiner extends DataMiner {167 /**168 * Mines data from databases169 */170 private String connection;171
172 @Override173 protected void openSource(String source) {174 System.out.println(" 🗄️ Connecting to database: " + source);175 this.connection = source;176 }177
178 @Override179 protected List<Map<String, Object>> extractData() {180 System.out.println(" 🔍 Executing SQL query...");181 return Arrays.asList(182 Map.of("order_id", 1001, "amount", 99.99, "status", "completed"),183 Map.of("order_id", 1002, "amount", 149.99, "status", "pending"),184 Map.of("order_id", 1003, "amount", 29.99, "status", "completed"),185 Map.of("order_id", 1004, "amount", 199.99, "status", "cancelled")186 );187 }188
189 @Override190 protected void closeSource() {191 System.out.println(" 🔒 Closing database connection");192 this.connection = null;193 }194
195 // Override hook to add custom behavior196 @Override197 protected void hookBeforeAnalysis(List<Map<String, Object>> data) {198 System.out.println(" ✅ Validating database records...");199 long invalidCount = data.stream()200 .filter(d -> "cancelled".equals(d.get("status")))201 .count();202 if (invalidCount > 0) {203 System.out.println(" ⚠️ Found " + invalidCount + " cancelled orders");204 }205 }206}207
208// Step 4: Use the pattern209public class Main {210 public static void main(String[] args) {211 System.out.println("=".repeat(60));212 System.out.println("Data Mining Pipeline (Template Method Pattern)");213 System.out.println("=".repeat(60));214
215 // Mine CSV data216 DataMiner csvMiner = new CSVDataMiner();217 csvMiner.mine("users.csv");218
219 // Mine JSON API data220 DataMiner apiMiner = new JSONAPIMiner();221 apiMiner.mine("https://api.example.com/users");222
223 // Mine Database data (with custom hook)224 DataMiner dbMiner = new DatabaseMiner();225 dbMiner.mine("postgresql://localhost/orders");226
227 System.out.println("\n" + "=".repeat(60));228 System.out.println("✅ Template Method Pattern: Same process, different sources!");229 System.out.println("✅ Common logic in base class - no duplication!");230 System.out.println("✅ Hooks allow optional customization!");231 }232}Template Method Pattern Variants
Section titled “Template Method Pattern Variants”1. With Hooks
Section titled “1. With Hooks”Hooks are optional methods that can be overridden:
1# Template Method with Hooks2class Beverage(ABC):3 def prepare_recipe(self):4 self.boil_water()5 self.brew()6 self.pour_in_cup()7
8 # Hook - optional step9 if self.customer_wants_condiments():10 self.add_condiments()11
12 # Hook with default behavior13 def customer_wants_condiments(self) -> bool:14 """Hook - subclasses can override to change behavior"""15 return True # Default: yes16
17class TeaWithHook(Beverage):18 def customer_wants_condiments(self) -> bool:19 # Ask the customer20 answer = input("Would you like lemon? (y/n): ")21 return answer.lower() == 'y'1// Template Method with Hooks2abstract class Beverage {3 public final void prepareRecipe() {4 boilWater();5 brew();6 pourInCup();7
8 // Hook - optional step9 if (customerWantsCondiments()) {10 addCondiments();11 }12 }13
14 // Hook with default behavior15 protected boolean customerWantsCondiments() {16 // Default: yes17 return true;18 }19
20 protected abstract void brew();21 protected abstract void addCondiments();22}23
24class TeaWithHook extends Beverage {25 @Override26 protected boolean customerWantsCondiments() {27 // Could ask the customer28 return true;29 }30}Hooks allow:
- Optional customization without forcing implementation
- Conditional execution of steps
- Default behavior that can be changed
2. Hollywood Principle
Section titled “2. Hollywood Principle”“Don’t call us, we’ll call you” - Base class calls subclass methods:
sequenceDiagram
participant Client
participant BaseClass
participant SubClass
Client->>BaseClass: templateMethod()
BaseClass->>BaseClass: step1()
BaseClass->>SubClass: abstractStep2()
Note right of SubClass: Subclass method called by base
SubClass-->>BaseClass: return
BaseClass->>BaseClass: step3()
BaseClass-->>Client: done
When to Use Template Method Pattern?
Section titled “When to Use Template Method Pattern?”Use Template Method Pattern when:
✅ Common algorithm structure - Same steps, different implementations
✅ Code duplication - Same structure repeated in multiple classes
✅ Control extension points - Only certain steps can be customized
✅ Enforce invariant steps - Some steps must always happen
✅ Hollywood Principle - Framework calls user code
When NOT to Use Template Method Pattern?
Section titled “When NOT to Use Template Method Pattern?”Don’t use Template Method Pattern when:
❌ Algorithms are completely different - No common structure
❌ All steps need customization - Use Strategy instead
❌ Inheritance isn’t appropriate - Use composition
❌ Simple cases - Just a few steps, no duplication
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Making Template Method Overridable
Section titled “Mistake 1: Making Template Method Overridable”1# ❌ Bad: Template method can be overridden2class BadBase:3 def template_method(self): # Not protected!4 self.step1()5 self.step2()6 self.step3()7 # Subclasses can completely change the algorithm!8
9# ✅ Good: Template method is final (in concept)10class GoodBase(ABC):11 def template_method(self):12 """13 Template method - DO NOT OVERRIDE!14 Note: Python doesn't have 'final', but document it clearly.15 """16 self.step1()17 self.step2()18 self.step3()1// ❌ Bad: Template method can be overridden2class BadBase {3 public void templateMethod() { // Not final!4 step1();5 step2();6 step3();7 }8 // Subclasses can completely change the algorithm!9}10
11// ✅ Good: Template method is final12abstract class GoodBase {13 public final void templateMethod() { // final!14 step1();15 step2();16 step3();17 }18 // Subclasses cannot override the algorithm structure19}Mistake 2: Too Many Abstract Methods
Section titled “Mistake 2: Too Many Abstract Methods”1# ❌ Bad: Everything is abstract - defeats the purpose!2class BadBase(ABC):3 def template_method(self):4 self.step1()5 self.step2()6 self.step3()7 self.step4()8 self.step5()9
10 @abstractmethod11 def step1(self): pass12 @abstractmethod13 def step2(self): pass14 @abstractmethod15 def step3(self): pass16 @abstractmethod17 def step4(self): pass18 @abstractmethod19 def step5(self): pass20 # All steps abstract = no code reuse!21
22# ✅ Good: Mix of concrete and abstract methods23class GoodBase(ABC):24 def template_method(self):25 self.setup() # Concrete26 self.do_work() # Abstract27 self.cleanup() # Concrete28
29 def setup(self): # Common implementation30 print("Setting up...")31
32 @abstractmethod33 def do_work(self): pass # Customization point34
35 def cleanup(self): # Common implementation36 print("Cleaning up...")1// ❌ Bad: Everything is abstract - defeats the purpose!2abstract class BadBase {3 public final void templateMethod() {4 step1();5 step2();6 step3();7 step4();8 step5();9 }10
11 protected abstract void step1();12 protected abstract void step2();13 protected abstract void step3();14 protected abstract void step4();15 protected abstract void step5();16 // All steps abstract = no code reuse!17}18
19// ✅ Good: Mix of concrete and abstract methods20abstract class GoodBase {21 public final void templateMethod() {22 setup(); // Concrete23 doWork(); // Abstract24 cleanup(); // Concrete25 }26
27 protected void setup() { // Common implementation28 System.out.println("Setting up...");29 }30
31 protected abstract void doWork(); // Customization point32
33 protected void cleanup() { // Common implementation34 System.out.println("Cleaning up...");35 }36}Mistake 3: Not Using Hooks for Optional Steps
Section titled “Mistake 3: Not Using Hooks for Optional Steps”1# ❌ Bad: Forcing subclasses to implement optional steps2class BadBase(ABC):3 def template_method(self):4 self.required_step()5 self.optional_step() # Abstract - but not all need it!6
7 @abstractmethod8 def optional_step(self): pass # Forces empty implementations9
10# ✅ Good: Use hooks for optional steps11class GoodBase(ABC):12 def template_method(self):13 self.required_step()14 self.optional_hook() # Hook with default15
16 @abstractmethod17 def required_step(self): pass18
19 def optional_hook(self):20 """Hook - override only if needed"""21 pass # Default: do nothing1// ❌ Bad: Forcing subclasses to implement optional steps2abstract class BadBase {3 public final void templateMethod() {4 requiredStep();5 optionalStep(); // Abstract - but not all need it!6 }7
8 protected abstract void optionalStep(); // Forces empty implementations9}10
11// ✅ Good: Use hooks for optional steps12abstract class GoodBase {13 public final void templateMethod() {14 requiredStep();15 optionalHook(); // Hook with default16 }17
18 protected abstract void requiredStep();19
20 protected void optionalHook() {21 // Default: do nothing. Override only if needed.22 }23}Benefits of Template Method Pattern
Section titled “Benefits of Template Method Pattern”- Code Reuse - Common code in base class
- Algorithm Structure - Can’t be changed by subclasses
- Single Point of Change - Fix bugs once
- Controlled Extension - Only specific steps customizable
- Inversion of Control - Hollywood Principle
- DRY Principle - Don’t Repeat Yourself
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Template Method Pattern?
Section titled “What is Template Method Pattern?”Template Method Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class method, letting subclasses override specific steps without changing the algorithm’s structure.
Why Use It?
Section titled “Why Use It?”- ✅ Code reuse - Common steps in base class
- ✅ Algorithm structure - Fixed, can’t be changed
- ✅ Controlled extension - Only specific steps customizable
- ✅ DRY principle - No duplication
- ✅ Hollywood Principle - “Don’t call us, we’ll call you”
How It Works?
Section titled “How It Works?”- Create abstract base class - With template method
- Define template method - The algorithm skeleton
- Add concrete methods - Common implementations
- Add abstract methods - Customization points
- Add hooks - Optional customization
- Create subclasses - Implement abstract methods
Key Components
Section titled “Key Components”1Abstract Class (Template Method)2├── template_method() → defines algorithm3├── concrete_step() → common implementation4├── abstract_step() → subclass implements5└── hook() → optional overrideSimple Example
Section titled “Simple Example”1class Beverage(ABC):2 def prepare_recipe(self): # Template method3 self.boil_water() # Concrete4 self.brew() # Abstract5 self.pour_in_cup() # Concrete6 self.add_condiments() # Abstract7
8 def boil_water(self):9 print("Boiling water")10
11 @abstractmethod12 def brew(self): pass13
14 @abstractmethod15 def add_condiments(self): passWhen to Use?
Section titled “When to Use?”✅ Common algorithm structure
✅ Code duplication in algorithm
✅ Need to control extension points
✅ Invariant steps must happen
✅ Framework development
When NOT to Use?
Section titled “When NOT to Use?”❌ Completely different algorithms
❌ All steps need customization
❌ Simple cases with no duplication
Key Takeaways
Section titled “Key Takeaways”- Template Method = Algorithm skeleton in base class
- Abstract methods = Customization points
- Concrete methods = Common implementation
- Hooks = Optional customization
- Hollywood Principle = Base calls subclass methods
Interview Focus: Template Method Pattern
Section titled “Interview Focus: Template Method Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Template Method Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class method, letting subclasses override specific steps without changing the algorithm’s structure. It promotes code reuse and enforces a consistent algorithm structure.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of inheritance-based patterns
- Indicates you can explain concepts clearly
2. Template Method vs Strategy Pattern
Section titled “2. Template Method vs Strategy Pattern”Must discuss:
- Template Method - Uses inheritance, algorithm structure fixed
- Strategy - Uses composition, entire algorithm swapped
- Key difference - Template customizes steps, Strategy replaces algorithm
Example to give:
“Template Method is like a form with blanks to fill in - the structure is fixed, you just provide specific content. Strategy is like choosing a completely different form. Template Method changes parts of an algorithm; Strategy changes the entire algorithm.”
3. Hollywood Principle
Section titled “3. Hollywood Principle”Must explain:
- “Don’t call us, we’ll call you”
- Base class calls subclass methods (inversion of control)
- Framework pattern - framework controls flow
Example to give:
“In Template Method, the base class controls when subclass methods are called - this is the Hollywood Principle. The subclass doesn’t call the base class to get things done; instead, the base class calls down to the subclass when it needs specific implementation.”
4. Benefits and Trade-offs
Section titled “4. Benefits and Trade-offs”Benefits to mention:
- Code reuse - Common code in base class
- Consistent structure - Algorithm can’t be altered
- Single point of change - Fix bugs once
- Controlled extension - Limited customization points
- DRY principle - No duplication
Trade-offs to acknowledge:
- Inheritance limitation - Java single inheritance
- Harder to understand - Need to trace through hierarchy
- Tight coupling - Subclasses tied to base class structure
- Fragile base class - Changes to base affect all
5. Common Interview Questions
Section titled “5. Common Interview Questions”Q: “When would you use Template Method over Strategy?”
A:
“I’d use Template Method when I have a fixed algorithm structure with specific steps that vary. For example, a data processing pipeline where opening, transforming, and closing are always the same, but extracting data varies by source. I’d use Strategy when I want to swap the entire algorithm, like different sorting algorithms that have completely different approaches.”
Q: “What are hooks in Template Method?”
A:
“Hooks are methods with default (usually empty) implementations that subclasses CAN override but don’t have to. Unlike abstract methods which MUST be overridden, hooks are optional. They provide additional customization points without forcing implementation. For example, a hook before saving that’s empty by default but can add validation.”
Q: “How does Template Method relate to SOLID principles?”
A:
“Template Method supports Open/Closed Principle - the algorithm structure is closed for modification but open for extension via abstract methods. It can violate Single Responsibility if the base class does too much. It demonstrates Dependency Inversion at the method level - the high-level algorithm depends on abstractions (abstract methods), not concrete implementations.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Template Method Pattern clearly
- Explain the difference from Strategy Pattern
- Describe the Hollywood Principle
- Implement Template Method from scratch
- List benefits and trade-offs
- Explain hooks vs abstract methods
- Give 2-3 real-world examples
- Discuss when NOT to use it
- Connect to SOLID principles
Remember: Template Method Pattern is about defining the skeleton of an algorithm - let subclasses fill in the details without changing the structure! 📋