
设计模式是软件开发中被讨论最多、也最容易被误用的主题之一。很多人能背出 23 种 GoF 模式的定义,却在真实项目中不知道何时该用、如何用好。更糟的是,有人把设计模式当作炫技工具,为了"用模式"而用模式,结果代码比不用还复杂。设计模式的本质是前人经验的总结,是解决特定问题的通用方案。本文将从实战角度,讲解如何在真实场景中恰当运用设计模式。
一、重新理解设计模式
设计模式不是目的,而是手段
模式的目的是让代码更易扩展、更易维护、更易理解。如果引入模式后代码反而更难懂,那就是误用。
模式的分类(GoF):
- 创建型:处理对象创建,如工厂、单例、建造者
- 结构型:处理对象组合,如适配器、装饰器、代理
- 行为型:处理对象交互,如策略、观察者、模板方法
模式的核心思想:
- 封装变化:把易变的部分隔离出来
- 面向接口:依赖抽象而非具体
- 组合优于继承:灵活组合行为
二、创建型模式实战
1. 工厂模式(Factory)
场景:需要根据条件创建不同类型的对象。
python
# 不用模式:调用方需要知道所有类
def create_notifier(channel):
if channel == "email":
return EmailNotifier()
elif channel == "sms":
return SmsNotifier()
elif channel == "wechat":
return WeChatNotifier()
# 使用工厂:注册式,易扩展
class NotifierFactory:
_registry = {}
@classmethod
def register(cls, channel, notifier_cls):
cls._registry[channel] = notifier_cls
@classmethod
def create(cls, channel):
if channel not in cls._registry:
raise ValueError(f"未知渠道: {channel}")
return cls._registry[channel]()
NotifierFactory.register("email", EmailNotifier)
NotifierFactory.register("sms", SmsNotifier)
收益:新增渠道只需注册,无需修改工厂代码,符合开闭原则。
2. 建造者模式(Builder)
场景:创建复杂对象,参数众多,部分可选。
python
# 反模式:参数爆炸
class HttpRequest:
def __init__(self, url, method="GET", headers=None,
body=None, timeout=30, retries=3,
auth=None, proxy=None):
...
# 建造者模式:链式调用,清晰
class HttpRequestBuilder:
def __init__(self, url):
self._url = url
self._method = "GET"
self._headers = {}
self._timeout = 30
def method(self, method):
self._method = method
return self
def header(self, key, value):
self._headers[key] = value
return self
def timeout(self, seconds):
self._timeout = seconds
return self
def build(self):
return HttpRequest(
url=self._url,
method=self._method,
headers=self._headers,
timeout=self._timeout,
)
# 使用
request = (HttpRequestBuilder("https://api.example.com")
.method("POST")
.header("Content-Type", "application/json")
.timeout(60)
.build())
收益:创建过程清晰,参数可选,可校验。
3. 单例模式(Singleton)
场景:全局唯一实例,如配置、连接池。
python
# Python 推荐方式:模块级单例
# config.py
class Config:
def __init__(self):
self.debug = False
self.db_url = "..."
config = Config() # 模块导入即单例
# 或使用装饰器
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabasePool:
...
警告:单例是全局状态,测试困难、并发风险。能用依赖注入就别用单例。
三、结构型模式实战
1. 适配器模式(Adapter)
场景:集成第三方库,接口不兼容。
python
# 内部统一接口
class PaymentGateway(ABC):
@abstractmethod
def pay(self, amount: Money, order_id: str) -> PaymentResult:
...
# 适配支付宝
class AlipayAdapter(PaymentGateway):
def __init__(self, alipay_client):
self._client = alipay_client
def pay(self, amount, order_id):
# 转换为支付宝 API 格式
response = self._client.trade_pay(
out_trade_no=order_id,
total_amount=str(amount.amount),
)
# 转换为内部结果
return PaymentResult(
success=response["code"] == "10000",
transaction_id=response["trade_no"],
)
收益:隔离外部变化,内部代码不受第三方影响。
2. 装饰器模式(Decorator)
场景:动态给对象添加功能,不修改原类。
python
class DataSource(ABC):
@abstractmethod
def read(self) -> str:
...
class FileSource(DataSource):
def read(self):
return "raw data"
# 装饰器:加密
class EncryptedSource(DataSource):
def __init__(self, wrapped: DataSource):
self._wrapped = wrapped
def read(self):
return decrypt(self._wrapped.read())
# 装饰器:压缩
class CompressedSource(DataSource):
def __init__(self, wrapped: DataSource):
self._wrapped = wrapped
def read(self):
return decompress(self._wrapped.read())
# 组合使用
source = EncryptedSource(CompressedSource(FileSource()))
data = source.read()
收益:功能可自由组合,符合单一职责。
3. 代理模式(Proxy)
场景:控制对象访问,如懒加载、权限、缓存。
python
class ImageProxy:
def __init__(self, filename):
self._filename = filename
self._real_image = None
def display(self):
# 懒加载:首次访问才创建真实对象
if self._real_image is None:
self._real_image = RealImage(self._filename)
self._real_image.display()
四、行为型模式实战
1. 策略模式(Strategy)
场景:多种算法可互换。
python
# 反模式:if-else 堆积
def calculate_shipping(order, method):
if method == "standard":
return 5
elif method == "express":
return 15
elif method == "same_day":
return 30
# 新增方式要改这里
# 策略模式
class ShippingStrategy(ABC):
@abstractmethod
def calculate(self, order) -> Money:
...
class StandardShipping(ShippingStrategy):
def calculate(self, order):
return Money(5)
class ExpressShipping(ShippingStrategy):
def calculate(self, order):
return Money(15)
class ShippingCalculator:
def __init__(self):
self._strategies = {}
def register(self, name, strategy):
self._strategies[name] = strategy
def calculate(self, name, order):
return self._strategies[name].calculate(order)
收益:新增策略无需修改计算器,符合开闭原则。
2. 观察者模式(Observer)
场景:对象状态变化,通知多个依赖者。
python
class Order:
def __init__(self):
self._observers = []
self._status = None
def attach(self, observer):
self._observers.append(observer)
def set_status(self, status):
self._status = status
self._notify()
def _notify(self):
for observer in self._observers:
observer.update(self)
class EmailNotifier:
def update(self, order):
send_email(f"订单状态: {order._status}")
class InventoryService:
def update(self, order):
if order._status == "PAID":
deduct_inventory(order)
order = Order()
order.attach(EmailNotifier())
order.attach(InventoryService())
order.set_status("PAID")
收益:发布者与订阅者解耦,易扩展。
3. 模板方法模式(Template Method)
场景:算法骨架固定,部分步骤可变。
python
class DataImporter(ABC):
def import_data(self, source):
# 模板方法:定义流程
data = self.read(source)
parsed = self.parse(data)
validated = self.validate(parsed)
self.save(validated)
@abstractmethod
def read(self, source):
...
@abstractmethod
def parse(self, data):
...
def validate(self, data):
# 默认实现,子类可覆盖
return data
@abstractmethod
def save(self, data):
...
class CsvImporter(DataImporter):
def read(self, source):
return open(source).read()
def parse(self, data):
return [line.split(",") for line in data.splitlines()]
def save(self, data):
db.bulk_insert(data)
收益:复用流程,子类只关注差异。
4. 责任链模式(Chain of Responsibility)
场景:请求需经多个处理器。
python
class Handler(ABC):
def __init__(self):
self._next = None
def set_next(self, handler):
self._next = handler
return handler
def handle(self, request):
if self._next:
return self._next.handle(request)
return None
class AuthHandler(Handler):
def handle(self, request):
if not request.get("token"):
raise Unauthorized()
return super().handle(request)
class RateLimitHandler(Handler):
def handle(self, request):
if is_rate_limited(request["ip"]):
raise TooManyRequests()
return super().handle(request)
class ValidationHandler(Handler):
def handle(self, request):
validate(request)
return super().handle(request)
# 组装链
auth = AuthHandler()
auth.set_next(RateLimitHandler()).set_next(ValidationHandler())
auth.handle(request)
收益:处理器可插拔,顺序灵活。
五、模式误用与反模式
1. 过度设计
python
# 杀鸡用牛刀:一个简单函数用上工厂+策略+单例
class AddStrategy(Strategy):
def execute(self, a, b):
return a + b
factory = StrategyFactory()
factory.register("add", AddStrategy())
result = factory.get("add").execute(1, 2) # 就是 1+2
原则:github.com/kevinedsam/qaq/issues/226简单问题用简单方案。模式是应对复杂度的,不是制造复杂度的。
2. 模式堆砌
一个类同时用上十种模式,读起来像天书。模式应服务于可读性,而非炫技。
3. 忽视语言特性
很多模式在特定语言中有更简洁的实现:
- Python 的装饰器语法替代装饰器模式
- 函数是一等公民,策略模式可用函数替代
- Go 的接口隐式实现简化适配器
4. 生搬硬套
不是所有 if-else 都要改成策略,不是所有对象创建都要工厂。判断标准:变化是否频繁、扩展是否必要。
六、何时该用设计模式
信号:
- 代码中出现大量重复的 if-else/switch
- 新增功能需要修改多处代码
- 类之间耦合严重,改动牵一发动全身
- 需要动态组合行为
- 需要隔离外部依赖
决策流程:
- 识别变化点
- 判断变化频率
- 选择合适模式
- 小步重构,测试保护
- 评估收益,必要时回退
七、从模式到原则
设计模式背后是设计原则:
- SOLID:单一职责、开闭、里氏替换、接口隔离、依赖倒置
- DRY:不要重复自己
- KISS:保持简单
- YAGNI:你不会需要它
模式是原则的具象化。理解原则,比记住模式更重要。真正的高手,不刻意用模式,却处处体现模式的思想。
结语
设计模式是工具箱,不是 checklist。用得好,它让代码优雅、易扩展;用不好,它让代码复杂、难维护。关键在于理解问题本质,选择恰当方案。记住:最好的设计,是让后来者觉得理所当然的设计,而非让人惊叹"用了多少模式"的设计。模式为解决问题服务,而非相反。













