Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
378: The Template Method Pattern
I've seen this happen a dozen times during code reviews. You're building a system that handles different types of data imports—maybe from a CSV, a JSON API, and an XML feed. On the surface, the process is always the same: you open the file, validate the schema, transform the data, and save it to the database.
Here is a common way a developer might implement this. It looks clean at first, but it's actually a maintenance time bomb.
class DataImporter:
def import_data(self):
pass
class CSVImporter(DataImporter):
def import_data(self):
print("Opening CSV file...")
print("Validating CSV columns...")
print("Transforming CSV rows to objects...")
print("Saving to database...")
print("Closing CSV file...")
class JSONImporter(DataImporter):
def import_data(self):
print("Opening JSON API connection...")
print("Validating JSON schema...")
print("Transforming JSON keys to objects...")
print("Saving to database...")
print("Closing JSON connection...")
The Maintenance Trap of Duplicate Orchestration
The code above "works," but look closely at the import_data methods. Both classes are doing the exact same orchestration: Open → Validate → Transform → Save → Close.
Now, imagine your boss tells you that we need to add a professional logging layer and a performance timer around the entire import process. In the current setup, you have to go into CSVImporter, JSONImporter, and every other importer you've written to manually add those logs. If you miss one, you've got an inconsistent system. If you change the order of operations, you're hunting through five different files to make sure they all match. You've duplicated the algorithm, even though the algorithm is the same for everyone.
Defining the Skeleton with the Template Method
The Template Method pattern solves this by moving the "skeleton" of the algorithm into a base class. Instead of the subclasses deciding how to run the process, the base class dictates the sequence, and the subclasses simply fill in the blanks.
Here is how I would rewrite this. We define a "template method" (which I've called run_import) that is final in spirit—subclasses shouldn't touch it. Then, we define the specific steps as methods that the subclasses must implement.
from abc import ABC, abstractmethod
class DataImporter(ABC):
# This is the Template Method.
# It defines the fixed skeleton of the algorithm.
def run_import(self):
self._log_start()
self.open_source()
self.validate()
self.transform()
self.save_to_db()
self.close_source()
self._log_end()
def _log_start(self):
print("LOG: Starting import process...")
def _log_end(self):
print("LOG: Import process completed successfully.")
def save_to_db(self):
print("Saving to database (this is common logic for all importers)...")
@abstractmethod
def open_source(self):
pass
@abstractmethod
def validate(self):
pass
@abstractmethod
def transform(self):
pass
@abstractmethod
def close_source(self):
pass
class CSVImporter(DataImporter):
def open_source(self):
print("Opening CSV file...")
def validate(self):
print("Validating CSV columns...")
def transform(self):
print("Transforming CSV rows to objects...")
def close_source(self):
print("Closing CSV file...")
class JSONImporter(DataImporter):
def open_source(self):
print("Opening JSON API connection...")
def validate(self):
print("Validating JSON schema...")
def transform(self):
print("Transforming JSON keys to objects...")
def close_source(self):
print("Closing JSON connection...")
# Usage
csv = CSVImporter()
csv.run_import()
Notice what happened here. The CSVImporter no longer knows (or cares) that it needs to log the start and end or that it needs to call save_to_db. It only focuses on the parts that are unique to CSVs.
I call this the "Hollywood Principle": Don't call us, we'll call you. The base class calls the subclass methods when it's ready for them. If I want to add a timer to the whole process now, I only have to change one line of code in the DataImporter.run_import method, and every single importer in the system is instantly upgraded. That is the power of the Template Method.
📋 Practical Task
Build a Multi-Format Document Generator
You need to create a document generation system that supports both HTML and PDF outputs. Regardless of the format, the generation process must always follow these steps in order:
- Initialize the document metadata
- Create the header
- Generate the main body content
- Append the footer
- Finalize and save the file
Your Task:
- Create an abstract base class
DocumentGeneratorwith a template method calledgenerate_document()that orchestrates the five steps above. - Implement a method in the base class for
initialize_metadata()since this is identical for all documents (e.g., it just prints "Setting document metadata..."). - Define the remaining four steps as abstract methods.
- Create two subclasses:
HTMLGeneratorandPDFGenerator. Implement the abstract methods to print specific messages related to that format (e.g.,HTMLGenerator.create_header()should print "Creating <header> tags..."). - Instantiate both generators and call
generate_document()on each to verify the sequence is identical but the implementation details differ.
There are no comments for now.