设计模式实战——从"知道"到"用好"(设计模式实训教程刘伟) szthrk.cn

设计模式是软件开发中被讨论最多、也最容易被误用的主题之一。很多人能背出 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
  • 新增功能需要修改多处代码
  • 类之间耦合严重,改动牵一发动全身
  • 需要动态组合行为
  • 需要隔离外部依赖

决策流程:

  1. 识别变化点
  2. 判断变化频率
  3. 选择合适模式
  4. 小步重构,测试保护
  5. 评估收益,必要时回退

七、从模式到原则

设计模式背后是设计原则:

  • SOLID:单一职责、开闭、里氏替换、接口隔离、依赖倒置
  • DRY:不要重复自己
  • KISS:保持简单
  • YAGNI:你不会需要它

模式是原则的具象化。理解原则,比记住模式更重要。真正的高手,不刻意用模式,却处处体现模式的思想。

结语

设计模式是工具箱,不是 checklist。用得好,它让代码优雅、易扩展;用不好,它让代码复杂、难维护。关键在于理解问题本质,选择恰当方案。记住:最好的设计,是让后来者觉得理所当然的设计,而非让人惊叹"用了多少模式"的设计。模式为解决问题服务,而非相反。