Python 中的模板方法模式(Template Method Pattern)
模板方法模式是一种行为型设计模式,其核心目的是:
定义一个操作中的算法骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些步骤。
形象比喻:就像做菜的“模板配方”——整体步骤固定(准备材料 → 烹饪 → 装盘),但具体食材和调味方式可以由不同菜系的厨师(子类)来实现。
模板方法模式的优点
- 代码复用:算法骨架在父类中定义,避免子类重复实现
- 控制扩展:只允许子类重写特定步骤(钩子方法)
- 符合开闭原则:新增新变体只需新增子类
- 一致性:保证所有子类遵循相同流程
典型应用场景
- 框架中的生命周期方法(如 Flask 的请求处理、Django 的视图渲染)
- 数据处理管道(读取 → 清洗 → 转换 → 保存)
- 测试框架(setUp → runTest → tearDown)
- 游戏流程(初始化 → 运行 → 结束)
- 报告生成(收集数据 → 格式化 → 输出)
Python 实现示例:数据处理管道
from abc import ABC, abstractmethod
import time
# 抽象类(AbstractClass)—— 定义模板方法
class DataProcessor(ABC):
# 模板方法:定义算法骨架(不可被子类重写)
def process(self):
print("=== 开始数据处理流程 ===\n")
self.extract() # 步骤1:提取数据
self.transform() # 步骤2:转换数据
self.validate() # 步骤3:验证(可选钩子)
self.load() # 步骤4:加载数据
print("\n=== 数据处理完成 ===\n")
# 必须由子类实现的抽象方法(Primitive Operations)
@abstractmethod
def extract(self):
pass
@abstractmethod
def load(self):
pass
# 可选步骤:默认实现,子类可选择重写
def transform(self):
print("默认转换:无操作")
# 钩子方法(Hook):默认什么都不做,子类可重写控制流程
def validate(self):
print("默认验证:通过(可被子类重写)")
return True
# 具体子类1:处理 CSV 文件
class CSVProcessor(DataProcessor):
def extract(self):
print("从 CSV 文件提取数据...")
time.sleep(1)
self.data = ["row1", "row2", "row3"]
def transform(self):
print("转换:将数据转为大写")
self.data = [row.upper() for row in self.data]
def load(self):
print(f"加载到数据库:{self.data}")
# 具体子类2:处理 API 数据
class APIProcessor(DataProcessor):
def extract(self):
print("从 REST API 获取 JSON 数据...")
time.sleep(1.5)
self.data = {"users": ["Alice", "Bob"]}
def transform(self):
print("转换:提取用户名字列表")
self.data = self.data["users"]
def validate(self):
if len(self.data) == 0:
print("验证失败:数据为空!")
return False
print("验证通过:数据非空")
return True
def load(self):
print(f"加载到缓存:{self.data}")
# 客户端使用(无需关心具体实现)
if __name__ == "__main__":
print("处理 CSV 数据:")
csv_proc = CSVProcessor()
csv_proc.process()
print("\n" + "="*40 + "\n")
print("处理 API 数据:")
api_proc = APIProcessor()
api_proc.process() # 即使 validate 返回 False,也会继续执行(实际可加判断)
输出:
处理 CSV 数据:
=== 开始数据处理流程 ===
从 CSV 文件提取数据...
转换:将数据转为大写
默认验证:通过(可被子类重写)
加载到数据库:['ROW1', 'ROW2', 'ROW3']
=== 数据处理完成 ===
========================================
处理 API 数据:
=== 开始数据处理流程 ===
从 REST API 获取 JSON 数据...
转换:提取用户名字列表
验证通过:数据非空
加载到缓存:['Alice', 'Bob']
=== 数据处理完成 ===
Pythonic 简化版:函数式模板(不使用继承)
Python 支持高阶函数,可以用函数组合实现“模板”:
def template_process(extract_func, transform_func=None, load_func=None):
def wrapper(data_source):
print("=== 流程开始 ===")
data = extract_func(data_source)
if transform_func:
data = transform_func(data)
else:
print("跳过转换")
print("默认验证通过")
if load_func:
load_func(data)
print("=== 流程结束 ===\n")
return data
return wrapper
# 定义具体步骤函数
def extract_csv(source):
print(f"读取 CSV: {source}")
return ["a", "b", "c"]
def transform_upper(data):
return [x.upper() for x in data]
def load_db(data):
print(f"写入数据库: {data}")
# 使用
process_csv = template_process(extract_csv, transform_upper, load_db)
process_csv("sales.csv")
模板方法模式结构总结
| 角色 | 说明 |
|---|---|
| AbstractClass | 抽象类,定义模板方法 + 抽象/钩子方法 |
| template_method() | 算法骨架(final,不可重写) |
| primitive_operation() | 抽象方法,必须子类实现 |
| hook() | 钩子方法,默认空实现或默认行为 |
| ConcreteClass | 具体子类,实现抽象方法,可重写钩子 |
模板方法 vs 其他模式对比
| 模式 | 目的 | 扩展方式 | 典型场景 |
|---|---|---|---|
| 模板方法 | 定义算法骨架,子类实现步骤 | 继承 | 框架生命周期、处理流程 |
| 策略 | 算法整体可替换 | 组合 | 支付方式、排序算法 |
| 工厂方法 | 对象创建延迟到子类 | 继承 | 对象实例化 |
| 建造者 | 复杂对象分步构建 | 组合 | 对象配置 |
Python 中的实用建议
- 推荐使用继承实现:当流程固定、步骤明确时
- 避免过度使用:Python 更倾向“组合优于继承”,如果步骤完全可替换,用策略模式更好
- 钩子方法很实用:用于控制流程分支(如
should_validate()返回 bool) - 实际常见例子:
http.server.BaseHTTPRequestHandler的do_GET()、do_POST()unittest.TestCase的setUp()、tearDown()django.views.View的dispatch()方法
进阶:带流程控制的钩子
class AdvancedProcessor(DataProcessor):
def validate(self):
print("严格验证中...")
if not self.data:
print("验证失败,终止流程!")
raise ValueError("数据为空")
def process(self):
print("开始高级流程")
self.extract()
self.transform()
if not self.validate(): # 钩子控制是否继续
print("验证未通过,停止加载")
return
self.load()
模板方法模式是框架设计的核心模式之一,它确保了流程的一致性,同时给予子类足够的灵活性。
如果你想看更多实战例子(如 Web 框架请求处理模板、游戏主循环模板、报表生成模板),或者如何与钩子结合实现条件分支,欢迎继续问!