模式对比与选择
学习设计模式后,你可能会面临"何时使用哪个模式"的困惑。本章将对比相似的设计模式,帮助你做出正确的选择。
相似模式对比
创建型模式对比
| 模式 | 目的 | 何时使用 |
|---|---|---|
| 单例模式 | 确保唯一实例 | 需要全局唯一的资源(数据库连接池、配置管理器) |
| 工厂方法 | 创建对象 | 不知道具体类型,需要延迟创建 |
| 抽象工厂 | 创建产品族 | 需要创建相关的对象族(不同UI组件) |
| 建造者 | 构建复杂对象 | 对象有很多参数,需要分步骤构建 |
graph TB
A[创建型模式] --> B[单例]
A --> C[工厂方法]
A --> D[抽象工厂]
A --> E[建造者]
B --> B1[唯一实例]
B --> B1[全局访问]
C --> C1[单一产品]
C --> C2[延迟创建]
D --> D1[产品族]
D --> D2[相关对象]
E --> E1[复杂对象]
E --> E2[分步骤构建]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
工厂方法 vs 抽象工厂
# 工厂方法 - 创建单一产品
class PizzaFactory:
def create_pizza(self, pizza_type):
if pizza_type == "cheese":
return CheesePizza()
elif pizza_type == "pepperoni":
return PepperoniPizza()
# 抽象工厂 - 创建产品族
class UIFactory:
def create_button(self):
pass
def create_checkbox(self):
pass
class WindowsFactory(UIFactory):
def create_button(self):
return WindowsButton()
def create_checkbox(self):
return WindowsCheckbox()
class MacFactory(UIFactory):
def create_button(self):
return MacButton()
def create_checkbox(self):
return MacCheckbox()
选择标准: - 需要创建一种产品的多种类型 → 工厂方法 - 需要创建多个相关的产品 → 抽象工厂
结构型模式对比
| 模式 | 目的 | 何时使用 |
|---|---|---|
| 适配器 | 转换接口 | 集成不兼容的第三方API |
| 装饰器 | 添加功能 | 动态添加/删除功能 |
| 代理 | 控制访问 | 延迟加载、权限控制、缓存 |
| 外观 | 简化接口 | 简化复杂子系统 |
| 组合 | 树形结构 | 表达部分-整体层次 |
graph TB
A[结构型模式] --> B[适配器]
A --> C[装饰器]
A --> D[代理]
A --> E[外观]
A --> F[组合]
B --> B1[接口转换]
C --> C1[动态添加功能]
D --> D1[控制访问]
E --> E1[简化接口]
F --> F1[树形结构]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
适配器 vs 装饰器 vs 代理
# 适配器 - 转换接口
class ThirdPartyAPI:
def old_method(self, data):
print(f"旧接口: {data}")
class NewInterfaceAdapter:
def __init__(self, api):
self.api = api
def new_method(self, data):
# 转换接口
self.api.old_method(data)
# 装饰器 - 添加功能
class LoggerDecorator:
def __init__(self, component):
self.component = component
def operation(self):
print("日志: 开始操作")
result = self.component.operation()
print("日志: 操作完成")
return result
# 代理 - 控制访问
class DatabaseProxy:
def __init__(self, real_db):
self.real_db = real_db
self.cache = {}
def query(self, sql):
if sql in self.cache:
return self.cache[sql]
result = self.real_db.query(sql)
self.cache[sql] = result
return result
选择标准: - 接口不兼容 → 适配器 - 需要动态添加功能 → 装饰器 - 需要控制访问/延迟加载/缓存 → 代理
行为型模式对比
| 模式 | 目的 | 何时使用 |
|---|---|---|
| 观察者 | 一对多通知 | 事件驱动、发布订阅 |
| 策略 | 算法可互换 | 多种算法可替换 |
| 模板方法 | 算法骨架 | 算法结构相同,部分步骤不同 |
| 命令 | 封装请求 | 需要撤销、队列、延迟执行 |
| 迭代器 | 遍历集合 | 需要统一遍历方式 |
| 状态 | 随状态改变行为 | 对象行为随状态改变 |
graph TB
A[行为型模式] --> B[观察者]
A --> C[策略]
A --> D[模板方法]
A --> E[命令]
A --> F[迭代器]
A --> G[状态]
B --> B1[一对多通知]
C --> C1[算法可互换]
D --> D1[算法骨架]
E --> E1[封装请求]
F --> F1[遍历集合]
G --> G1[随状态改变]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
策略模式 vs 状态模式
# 策略模式 - 算法可互换
class SortStrategy:
def sort(self, data):
pass
class BubbleSort(SortStrategy):
def sort(self, data):
print("冒泡排序")
class QuickSort(SortStrategy):
def sort(self, data):
print("快速排序")
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def sort(self, data):
self.strategy.sort(data) # 策略可手动切换
# 状态模式 - 随状态改变行为
class OrderState:
def confirm(self):
pass
class PendingState(OrderState):
def confirm(self):
print("订单已确认")
return ConfirmedState()
class ConfirmedState(OrderState):
def confirm(self):
print("订单已确认")
return self
class Order:
def __init__(self):
self.state = PendingState()
def confirm(self):
self.state = self.state.confirm() # 状态自动转换
选择标准: - 算法可手动选择和替换 → 策略模式 - 行为随状态自动改变 → 状态模式
实际场景选择指南
场景1:需要创建对象
graph LR
A[需要创建对象] --> B{对象数量}
B -->|1个| C{需要唯一实例?}
B -->|多个| D{对象类型?}
C -->|是| E[单例模式]
C -->|否| F{对象复杂?}
F -->|是| G[建造者模式]
F -->|否| H[工厂方法模式]
D -->|1种类型| H
D -->|相关对象族| I[抽象工厂模式]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style E fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style G fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style H fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style I fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
场景2:需要扩展功能
graph LR
A[需要扩展功能] --> B{如何扩展}
B -->|接口不兼容| C[适配器模式]
B -->|添加功能| D{是否动态?}
D -->|是| E[装饰器模式]
D -->|否| F[继承]
B -->|控制访问| G[代理模式]
B -->|简化接口| H[外观模式]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style C fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style E fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style G fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style H fill:#fff9c4,stroke:#f9a825,stroke-width:2px
场景3:需要处理行为
graph LR
A[需要处理行为] --> B{需求}
B -->|通知多个对象| C[观察者模式]
B -->|算法可互换| D[策略模式]
B -->|算法骨架固定| E[模板方法模式]
B -->|封装请求| F[命令模式]
B -->|遍历集合| G[迭代器模式]
B -->|随状态改变| H[状态模式]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style C fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style D fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style E fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style F fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style G fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style H fill:#c8e6c9,stroke:#43a047,stroke-width:2px
组合使用多个模式
实际项目中,多个模式经常组合使用。
示例:电商系统
# 组合使用多个模式
class PaymentSystem:
def __init__(self):
# 单例模式 - 配置管理器
self.config = ConfigManager.getInstance()
# 工厂方法 - 创建支付方式
self.factory = PaymentFactory()
# 策略模式 - 支付策略
self.payment_strategy = None
# 观察者模式 - 支付通知
self.observers = []
# 模板方法 - 支付流程
def process_payment(self, payment_type, amount):
self.validate_payment()
self.execute_payment(payment_type, amount)
self.notify_observers()
# 装饰器模式 - 添加日志
def process_payment(self, payment_type, amount):
self._log_start()
# 原有逻辑
self._log_end()
# 组合关系说明:
# 1. 单例模式:配置管理器全局唯一
# 2. 工厂方法:创建不同支付方式
# 3. 策略模式:支付算法可替换
# 4. 观察者模式:支付完成后通知
# 5. 模板方法:支付流程骨架
# 6. 装饰器模式:添加日志功能
反模式与常见错误
❌ 错误1:过度设计
# 简单的加法也用模式?
class AddCommand:
def execute(self):
return self.x + self.y
class Calculator:
def add(self, x, y):
command = AddCommand(x, y)
return command.execute()
# ❌ 太复杂了!应该直接:
def add(x, y):
return x + y
❌ 错误2:模式滥用
# 为了用模式而用模式
class SingleCalculator:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def add(self, x, y):
return x + y
# 计算器不需要单例!用普通类或静态方法即可
❌ 错误3:忽略语言特性
# Python的@property比模式更简单
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@property
def area(self):
return 3.14 * self._radius ** 2
# 不需要代理模式
选择设计模式的步骤
第1步:识别问题
- 创建问题 - 需要创建对象吗?
- 结构问题 - 需要组合对象吗?
- 行为问题 - 需要处理交互吗?
第2步:分析需求
| 问题 | 关键词 | 推荐模式 |
|---|---|---|
| 创建对象 | 唯一、工厂、构建 | 单例、工厂方法、建造者 |
| 接口兼容 | 不兼容、转换 | 适配器 |
| 功能扩展 | 动态、添加 | 装饰器 |
| 访问控制 | 延迟、缓存、权限 | 代理 |
| 简化接口 | 复杂、子系统 | 外观 |
| 树形结构 | 部分-整体、层次 | 组合 |
| 算法切换 | 可互换、算法 | 策略 |
| 算法骨架 | 固定步骤、部分不同 | 模板方法 |
| 一对多通知 | 订阅、事件 | 观察者 |
| 封装请求 | 撤销、队列 | 命令 |
| 遍历集合 | 统一方式 | 迭代器 |
| 状态改变 | 状态相关行为 | 状态 |
第3步:考虑语言特性
Python有许多特性可以简化模式实现:
| 模式 | Python简化方式 |
|---|---|
| 单例 | 模块变量、类装饰器 |
| 工厂方法 | 简单工厂、@classmethod |
| 适配器 | 鸭子类型 |
| 装饰器 | @decorator语法 |
| 迭代器 | 生成器 |
| 策略 | 函数作为参数 |
第4步:评估复杂度
graph LR
A[问题复杂度] --> B{复杂?}
B -->|否| C[简单实现]
B -->|是| D{是否经常变化?}
D -->|否| E[考虑模式]
D -->|是| F[强烈建议模式]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style C fill:#ffcdd2,stroke:#c62828,stroke-width:2px
style E fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style F fill:#c8e6c9,stroke:#43a047,stroke-width:2px
模式选择清单
在选择模式前,问自己以下问题:
✅ 需要模式的情况
- [ ] 代码中有大量重复
- [ ] 需要频繁修改
- [ ] 多个地方有相似逻辑
- [ ] 测试困难
- [ ] 扩展需要修改现有代码
❌ 不需要模式的情况
- [ ] 问题很简单
- [ ] 代码不会变化
- [ ] 只在一个地方使用
- [ ] 模式增加的复杂度大于收益
本章要点
- ✅ 相似模式有不同目的和适用场景
- ✅ 根据问题类型选择合适的模式
- ✅ 多个模式可以组合使用
- ✅ 避免过度设计和模式滥用
- ✅ 利用Python语言特性简化实现
- ✅ 评估复杂度后再决定是否使用模式
下一步:实际项目案例 🚀