【设计模式与重构】设计模式
设计模式
发布时间:2020-12-25 00:40:00
原文链接:https://www.cnblogs.com/xine/p/14187051
结构图来源:refactoringguru.cn
设计模式(Design Pattern)是前人在大量软件实践中总结出的、可复用的面向对象设计方案。GoF(Gang of Four)在《设计模式:可复用面向对象软件的基础》一书中归纳了 23 个经典模式,按目的分为三大类:创建型、结构型、行为型。本文用 Python 逐一梳理这 22 个模式(Interpreter 不在 refactoringguru 收录范围内,故略),每个模式配一张 UML 结构图。
一、概述
| 类别 | 关注点 | 模式 |
|---|---|---|
| 创建型 | 对象怎么"造"出来 | 单例、原型、工厂方法、抽象工厂、建造者 |
| 结构型 | 类和对象怎么"组装" | 适配器、桥接、组合、装饰器、外观、享元、代理 |
| 行为型 | 对象之间怎么"协作" | 责任链、命令、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者 |
两条基本原则贯穿始终:面向接口编程,而不是面向实现编程;优先使用组合,而不是继承。
二、创建型模式
创建型模式把对象的创建过程封装起来,让程序在"谁来创建、创建什么、何时创建"上保持灵活。
2.1 单例模式(Singleton)
意图:保证一个类只有一个实例,并提供全局访问点。典型场景:配置管理器、连接池、日志对象。

Python 里最优雅的写法是借助模块天然的单例性,或用 __new__ 控制:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton()
b = Singleton()
print(a is b) # True
线程安全版本可以加锁:
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
注意:单例本质上是"全局状态",测试和并发场景要慎用,Python 中更推荐直接用模块级对象。
2.2 原型模式(Prototype)
意图:通过克隆已有实例来创建新对象,避免重复的初始化开销,或实现"快照"能力。

import copy
class Prototype:
def clone(self):
return copy.deepcopy(self)
def __init__(self, name, tags=None):
self.name = name
self.tags = tags if tags is not None else []
proto = Prototype("模板", tags=["a", "b"])
shallow = copy.copy(proto) # 浅拷贝:共享内部列表
deep = proto.clone() # 深拷贝:完全独立
deep.tags.append("c")
print(proto.tags) # ['a', 'b'],不受影响
适用场景:对象创建代价大(数据库/网络初始化);需要保存对象某一时刻的状态(如编辑器的撤销快照)。注意区分浅拷贝与深拷贝。
2.3 工厂方法模式(Factory Method)
意图:定义创建对象的接口,让子类决定实例化哪个具体类。把 new 的决定权从使用方移到子类。

from abc import ABC, abstractmethod
class Transport(ABC):
@abstractmethod
def deliver(self, cargo: str) -> str: ...
class Truck(Transport):
def deliver(self, cargo: str) -> str:
return f"陆运:{cargo} 装上卡车出发"
class Ship(Transport):
def deliver(self, cargo: str) -> str:
return f"海运:{cargo} 装上货轮出发"
class Logistics(ABC): # Creator
@abstractmethod
def create_transport(self) -> Transport: # 工厂方法
...
def plan_delivery(self, cargo: str) -> str:
transport = self.create_transport()
return transport.deliver(cargo)
class RoadLogistics(Logistics): # ConcreteCreator
def create_transport(self) -> Transport:
return Truck()
class SeaLogistics(Logistics):
def create_transport(self) -> Transport:
return Ship()
print(RoadLogistics().plan_delivery("钢琴"))
print(SeaLogistics().plan_delivery("钢琴"))
核心收益:新增一种运输方式时,只需新增一对类,原有代码(plan_delivery)一行不用改,符合开闭原则。
2.4 抽象工厂模式(Abstract Factory)
意图:创建一族相关的产品对象,而不指定具体类。工厂方法针对"一个产品",抽象工厂针对"一组产品"。

from abc import ABC, abstractmethod
# 抽象产品:按钮 和 复选框
class Button(ABC):
@abstractmethod
def paint(self) -> str: ...
class Checkbox(ABC):
@abstractmethod
def paint(self) -> str: ...
# Win 家族
class WinButton(Button):
def paint(self) -> str:
return "Windows 风格按钮"
class WinCheckbox(Checkbox):
def paint(self) -> str:
return "Windows 风格复选框"
# Mac 家族
class MacButton(Button):
def paint(self) -> str:
return "Mac 风格按钮"
class MacCheckbox(Checkbox):
def paint(self) -> str:
return "Mac 风格复选框"
# 抽象工厂:一次生产一整族 UI 组件
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button: ...
@abstractmethod
def create_checkbox(self) -> Checkbox: ...
class WinFactory(GUIFactory):
def create_button(self) -> Button:
return WinButton()
def create_checkbox(self) -> Checkbox:
return WinCheckbox()
class MacFactory(GUIFactory):
def create_button(self) -> Button:
return MacButton()
def create_checkbox(self) -> Checkbox:
return MacCheckbox()
class Application:
def __init__(self, factory: GUIFactory):
self.factory = factory
def render(self):
button = self.factory.create_button()
checkbox = self.factory.create_checkbox()
print(button.paint(), "|", checkbox.paint())
Application(MacFactory()).render() # Mac 风格按钮 | Mac 风格复选框
适用场景:系统要支持多个"产品族"(Win/Mac、浅色/深色主题、MySQL/PG 驱动组),且要求族内产品搭配使用。缺点是新增产品种类(不只是族)时要改所有工厂接口。
2.5 建造者模式(Builder)
意图:分步骤构造复杂对象,使"构建过程"与"表示"分离。当构造函数参数多、可选参数杂( telescoping constructor 噩梦)时尤其有用。

class Computer:
def __init__(self):
self.cpu = None
self.memory = None
self.disk = None
self.gpu = None # 可选
def __str__(self):
parts = [self.cpu, self.memory, self.disk, self.gpu]
return "Computer(" + ", ".join(p for p in parts if p) + ")"
class ComputerBuilder:
def __init__(self):
self._product = Computer()
def cpu(self, value):
self._product.cpu = value
return self # 支持链式调用
def memory(self, value):
self._product.memory = value
return self
def disk(self, value):
self._product.disk = value
return self
def gpu(self, value):
self._product.gpu = value
return self
def build(self) -> Computer:
if not self._product.cpu or not self._product.memory:
raise ValueError("CPU 和内存为必选组件")
product, self._product = self._product, Computer()
return product
pc = (ComputerBuilder()
.cpu("i9-14900K")
.memory("64G DDR5")
.disk("2T NVMe")
.gpu("RTX 4090")
.build())
print(pc) # Computer(i9-14900K, 64G DDR5, 2T NVMe, RTX 4090)
对比工厂模式:工厂关心"造什么",建造者关心"怎么一步步组装"。Python 中 dataclasses + 关键字参数也能缓解构造参数问题,但建造者仍适合有"组装顺序/校验规则"的场景。
三、结构型模式
结构型模式解决"如何把类和对象组合成更大的结构,同时保持灵活高效"。
3.1 适配器模式(Adapter)
意图:把一个类的接口转换成客户端期望的另一个接口,让不兼容的类可以合作。典型的"转接头"。

class LegacyPayment: # 遗留接口
def make_payment(self, amount_in_cents: int):
print(f"遗留系统支付 {amount_in_cents} 分")
class ModernPaymentGateway: # 新代码期望的接口
def pay(self, amount_yuan: float):
pass
class PaymentAdapter(ModernPaymentGateway):
def __init__(self, legacy: LegacyPayment):
self.legacy = legacy
def pay(self, amount_yuan: float): # 接口转换 + 单位转换
self.legacy.make_payment(int(amount_yuan * 100))
gateway = PaymentAdapter(LegacyPayment())
gateway.pay(9.9) # 遗留系统支付 990 分
3.2 桥接模式(Bridge)
意图:把"抽象"和"实现"放在两个独立的维度上分别继承/组合,避免类爆炸(如 形状×渲染方式 的笛卡尔积)。

from abc import ABC, abstractmethod
class Renderer(ABC): # 实现维度
@abstractmethod
def render_circle(self, radius: float): ...
class VectorRenderer(Renderer):
def render_circle(self, radius: float):
print(f"画矢量圆,半径 {radius}")
class RasterRenderer(Renderer):
def render_circle(self, radius: float):
print(f"逐像素画圆,半径 {radius}")
class Shape(ABC): # 抽象维度,持有实现引用(桥)
def __init__(self, renderer: Renderer):
self.renderer = renderer
@abstractmethod
def draw(self): ...
class Circle(Shape):
def __init__(self, renderer: Renderer, radius: float):
super().__init__(renderer)
self.radius = radius
def draw(self):
self.renderer.render_circle(self.radius)
Circle(VectorRenderer(), 5).draw() # 画矢量圆,半径 5
Circle(RasterRenderer(), 5).draw() # 逐像素画圆,半径 5
新增形状或新增渲染器,各自扩展即可,2×N 的组合不再需要 N 倍的子类。
3.3 组合模式(Composite)
意图:把对象组成树形结构,让客户端用统一的方式处理单个对象和对象容器。文件系统是最典型的例子。

from abc import ABC, abstractmethod
class Component(ABC):
@abstractmethod
def size(self) -> int: ...
class File(Component): # 叶子节点
def __init__(self, name: str, size: int):
self.name = name
self.size_ = size
def size(self) -> int:
return self.size_
class Folder(Component): # 容器节点
def __init__(self, name: str):
self.name = name
self.children = []
def add(self, component: Component):
self.children.append(component)
def size(self) -> int:
return sum(c.size() for c in self.children)
root = Folder("root")
src = Folder("src")
src.add(File("main.py", 10))
src.add(File("utils.py", 5))
root.add(src)
root.add(File("README.md", 1))
print(root.size()) # 16,整棵树自动递归求和
3.4 装饰器模式(Decorator)
意图:不修改原类的前提下,动态给对象叠加新职责。Python 的 @decorator 语法糖名字来源于此思想,但两者并不等价。

from abc import ABC, abstractmethod
class DataSource(ABC):
@abstractmethod
def write(self, data: str): ...
class FileDataSource(DataSource): # 被装饰的核心对象
def __init__(self, filename: str):
self.filename = filename
def write(self, data: str):
print(f"写入 {self.filename}: {data!r}")
class DataSourceDecorator(DataSource): # 装饰器基类,同接口
def __init__(self, wrappee: DataSource):
self.wrappee = wrappee
def write(self, data: str):
self.wrappee.write(data)
class CompressDecorator(DataSourceDecorator):
def write(self, data: str):
print("[先压缩]")
self.wrappee.write(data)
class EncryptDecorator(DataSourceDecorator):
def write(self, data: str):
self.wrappee.write(data[::-1]) # 简单示意:倒序=加密
file = FileDataSource("data.txt")
stream = EncryptDecorator(CompressDecorator(file))
stream.write("hello")
# [先压缩]
# 写入 data.txt: 'olleh'
装饰器可以像洋葱一样层层叠加,比"为每种组合建一个子类"灵活得多。
3.5 外观模式(Facade)
意图:为复杂子系统提供一个简化的统一入口。它不是加功能,而是"减负"。

class CPU:
def freeze(self): print("CPU 冻结")
def jump(self, addr): print(f"CPU 跳转到 {addr:#x}")
def execute(self): print("CPU 执行")
class Memory:
def load(self, data): print("内存加载数据")
class Disk:
def read(self, sector): print(f"磁盘读取扇区 {sector}")
class ComputerFacade: # 一行代码开机
def __init__(self):
self.cpu, self.memory, self.disk = CPU(), Memory(), Disk()
def start(self):
self.cpu.freeze()
self.memory.load(self.disk.read(0))
self.cpu.jump(0)
self.cpu.execute()
ComputerFacade().start()
3.6 享元模式(Flyweight)
意图:共享大量细粒度对象的公共状态,用极小的内存表示海量对象。核心是分离内部状态(可共享)与外部状态(不可共享)。

class TreeType: # 享元:内部状态(种类、颜色、纹理)
def __init__(self, name, color, texture):
self.name, self.color, self.texture = name, color, texture
def draw(self, x, y): # 外部状态由调用方传入
print(f"{self.color}的{self.name} 画在 ({x},{y})")
class TreeFactory:
_types = {}
@classmethod
def get_tree_type(cls, name, color, texture):
key = (name, color, texture)
if key not in cls._types:
cls._types[key] = TreeType(name, color, texture)
return cls._types[key]
class Forest:
def __init__(self):
self.trees = [] # (treeType, x, y)
def plant(self, name, color, texture, x, y):
ttype = TreeFactory.get_tree_type(name, color, texture)
self.trees.append((ttype, x, y))
def draw(self):
for ttype, x, y in self.trees:
ttype.draw(x, y)
forest = Forest()
for i in range(10000): # 10000 棵树只共享少数几个 TreeType
forest.plant("松树", "深绿", "针叶纹理", i % 100, i // 100)
Python 中 sys.intern、字符串驻留、__slots__ 都是这种"省内存"思想的体现。
3.7 代理模式(Proxy)
意图:提供一个与原对象同接口的"替身",在访问前后插入控制逻辑:延迟加载、权限校验、缓存、远程调用等。

import time
class HeavyService:
def __init__(self):
time.sleep(2) # 模拟昂贵的初始化
print("真实服务初始化完成")
def request(self):
return "真实响应"
class LazyProxy: # 虚代理:用到才加载
def __init__(self):
self._service = None
def request(self):
if self._service is None:
self._service = HeavyService()
return self._service.request()
proxy = LazyProxy()
print("代理已创建,但服务还没初始化")
print(proxy.request()) # 此刻才真正初始化
print(proxy.request()) # 第二次直接复用
与装饰器的区别:装饰器叠加以增强行为,代理控制对目标的访问;二者结构相同,意图不同。
四、行为型模式
行为型模式关注对象间的职责分配与通信方式。
4.1 责任链模式(Chain of Responsibility)
意图:让多个处理器组成一条链,请求沿链传递,每个处理器决定"处理它"或"传给下一个"。Web 中间件、审批流都是它。

from abc import ABC, abstractmethod
class Handler(ABC):
def __init__(self, next=None):
self.next = next
def handle(self, request: str):
if self.can_handle(request):
self.do_handle(request)
elif self.next:
self.next.handle(request)
else:
print(f"没人能处理请求:{request}")
@abstractmethod
def can_handle(self, request: str) -> bool: ...
@abstractmethod
def do_handle(self, request: str): ...
class MonkeyHandler(Handler):
def can_handle(self, r): return r == "香蕉"
def do_handle(self, r): print("猴子处理了香蕉")
class DogHandler(Handler):
def can_handle(self, r): return r == "骨头"
def do_handle(self, r): print("狗处理了骨头")
chain = MonkeyHandler(DogHandler())
chain.handle("香蕉") # 猴子处理了香蕉
chain.handle("骨头") # 狗处理了骨头
chain.handle("鱼") # 没人能处理请求:鱼
4.2 命令模式(Command)
意图:把"请求"封装成对象,从而支持排队、日志、撤销/重做。GUI 按钮、编辑器撤销栈的核心模式。

from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self): ...
def undo(self): ...
class Editor:
def __init__(self):
self.text = ""
def insert(self, s): self.text += s
def delete(self, n): self.text = self.text[:-n]
class InsertCommand(Command):
def __init__(self, editor: Editor, s: str):
self.editor, self.s = editor, s
def execute(self):
self.editor.insert(self.s)
def undo(self):
self.editor.delete(len(self.s))
class History: # 调用者:只认识 Command
def __init__(self):
self.stack = []
def run(self, cmd: Command):
cmd.execute()
self.stack.append(cmd)
def undo(self):
if self.stack:
self.stack.pop().undo()
editor, history = Editor(), History()
history.run(InsertCommand(editor, "hello "))
history.run(InsertCommand(editor, "world"))
print(editor.text) # hello world
history.undo()
print(editor.text) # hello
在 Python 中,一个带 __call__ 的闭包类或函数对象同样可以充当命令。
4.3 迭代器模式(Iterator)
意图:顺序遍历集合元素,而不暴露集合的内部表示。Python 把它做成了语言级协议:__iter__ / __next__(以及 for 循环、生成器)。

class BinaryTree:
def __init__(self, value, left=None, right=None):
self.value, self.left, self.right = value, left, right
def __iter__(self):
"""中序遍历——用生成器实现迭代器,最 Pythonic 的方式"""
if self.left:
yield from self.left
yield self.value
if self.right:
yield from self.right
tree = BinaryTree(2,
left=BinaryTree(1),
right=BinaryTree(3))
for v in tree: # 集合内部是树,客户端却像遍历列表一样简单
print(v, end=" ") # 1 2 3
4.4 中介者模式(Mediator)
意图:把网状的对象互调收敛为"都只跟中介说话",减少对象间的耦合。聊天室、机场塔台、MVC 中的 Controller。

from abc import ABC, abstractmethod
class ChatRoom: # 中介者
def __init__(self):
self.users = []
def register(self, user):
self.users.append(user)
user.room = self
def broadcast(self, sender, message: str):
for u in self.users:
if u is not sender:
u.receive(f"[{sender.name}] {message}")
class User: # 同事类:只认识中介
def __init__(self, name: str):
self.name = name
self.room = None
def send(self, message: str):
self.room.broadcast(self, message)
def receive(self, message: str):
print(f"{self.name} 收到:{message}")
room = ChatRoom()
alice, bob = User("Alice"), User("Bob")
room.register(alice)
room.register(bob)
alice.send("大家好") # Bob 收到:[Alice] 大家好
4.5 备忘录模式(Memento)
意图:在不破坏封装的前提下,捕获对象内部状态并存档,以便之后恢复。与命令模式配合可实现完整的撤销体系。

from dataclasses import dataclass
@dataclass(frozen=True) # 不可变快照
class Memento:
state: str
class Originator: # 原发器
def __init__(self, state: str):
self._state = state
def do(self, action: str):
self._state += action
def save(self) -> Memento:
return Memento(self._state)
def restore(self, memento: Memento):
self._state = memento.state
def __str__(self):
return self._state
class Caretaker: # 管理者:只存快照,不看不改
def __init__(self):
self._history = []
def backup(self, memento: Memento):
self._history.append(memento)
def undo(self) -> Memento:
return self._history.pop()
doc = Originator("第一章")
history = Caretaker()
history.backup(doc.save())
doc.do(",第二节")
history.backup(doc.save())
doc.do(",第三节")
print(doc) # 第一章,第二节,第三节
doc.restore(history.undo())
print(doc) # 第一章,第二节
4.6 观察者模式(Observer)
意图:定义一对多的订阅关系,状态变化时自动通知所有订阅者。事件系统、消息队列、MVVM 数据绑定的基石。

class Event:
"""极简事件总线:任何人可订阅,发布时全部回调"""
def __init__(self):
self._subscribers = []
def subscribe(self, callback):
self._subscribers.append(callback)
def publish(self, data):
for callback in self._subscribers:
callback(data)
class PriceTicker:
def __init__(self):
self.price_changed = Event()
def set_price(self, new_price: float):
self.price_changed.publish(new_price)
ticker = PriceTicker()
ticker.price_changed.subscribe(lambda p: print(f"短信通知:价格变为 {p}"))
ticker.price_changed.subscribe(lambda p: print(f"图表刷新:{p:.2f}"))
ticker.set_price(102.5)
# 短信通知:价格变为 102.5
# 图表刷新:102.50
4.7 状态模式(State)
意图:把"某状态下的一整套行为"封装成状态类,用替换状态对象代替大量的 if/elif 状态判断。

from abc import ABC, abstractmethod
class State(ABC):
@abstractmethod
def play(self, player) -> str: ...
@abstractmethod
def pause(self, player) -> str: ...
class PlayingState(State):
def play(self, player): return "已在播放中"
def pause(self, player):
player.state = PausedState()
return "暂停播放"
class PausedState(State):
def play(self, player):
player.state = PlayingState()
return "继续播放"
def pause(self, player): return "已处于暂停"
class LockedState(State):
def play(self, player): return "先解锁再操作"
def pause(self, player): return "先解锁再操作"
class AudioPlayer:
def __init__(self):
self.state = LockedState()
def click_play(self): return self.state.play(self)
def click_pause(self): return self.state.pause(self)
def unlock(self):
self.state = PausedState()
return "解锁"
player = AudioPlayer()
print(player.click_play()) # 先解锁再操作
player.unlock()
print(player.click_play()) # 继续播放
print(player.click_pause()) # 暂停播放
4.8 策略模式(Strategy)
意图:定义一族可互换的算法,运行时按需切换,消除大量条件分支。

from abc import ABC, abstractmethod
class DiscountStrategy(ABC):
@abstractmethod
def apply(self, price: float) -> float: ...
class NoDiscount(DiscountStrategy):
def apply(self, price): return price
class HalfPrice(DiscountStrategy):
def apply(self, price): return price * 0.5
class FullReduction(DiscountStrategy): # 满100减30
def apply(self, price):
return price - (price // 100) * 30
class Order:
def __init__(self, price: float, strategy: DiscountStrategy):
self.price = price
self.strategy = strategy
def checkout(self) -> float:
return self.strategy.apply(self.price)
print(Order(250, FullReduction()).checkout()) # 160.0
print(Order(250, HalfPrice()).checkout()) # 125.0
Python 里策略还可以直接用函数传递——比类更轻量,但需要"一组有共享上下文的策略"时,类形式更整洁。
4.9 模板方法模式(Template Method)
意图:父类定义算法骨架(固定步骤顺序),把某些步骤延迟到子类实现。数据管道、测试框架 setUp/run/tearDown 的套路。

from abc import ABC, abstractmethod
class DataPipeline(ABC):
def run(self): # 模板方法:固定流程
raw = self.extract()
cleaned = self.transform(raw)
self.load(cleaned)
print("流水线执行完毕\n")
@abstractmethod
def extract(self): ... # 子类必须实现
@abstractmethod
def transform(self, data): ...
def load(self, data): # 钩子:可选覆盖
print(f"写入目标:{data}")
class EtlCsv(DataPipeline):
def extract(self): return "csv原始数据"
def transform(self, data): return data.upper()
class EtlApi(DataPipeline):
def extract(self): return "api的json"
def transform(self, data): return data + "(已校验)"
def load(self, data): # 自定义加载
print(f"POST 写入远程:{data}")
EtlCsv().run()
EtlApi().run()
4.10 访问者模式(Visitor)
意图:在不修改对象结构的前提下,为结构中的元素添加新操作。把"数据结构"和"作用于结构的算法"分开(双分派)。

from abc import ABC, abstractmethod
class Visitor(ABC):
@abstractmethod
def visit_circle(self, circle): ...
@abstractmethod
def visit_square(self, square): ...
class Shape(ABC):
@abstractmethod
def accept(self, visitor: Visitor): ...
class Circle(Shape):
def __init__(self, radius): self.radius = radius
def accept(self, visitor):
visitor.visit_circle(self) # 第一次分派
class Square(Shape):
def __init__(self, side): self.side = side
def accept(self, visitor):
visitor.visit_square(self)
class AreaVisitor(Visitor): # 新操作一:算面积
def visit_circle(self, c): return 3.14159 * c.radius ** 2
def visit_square(self, s): return s.side ** 2
class ExportVisitor(Visitor): # 新操作二:导出 JSON,无需改 Shape
def visit_circle(self, c): return f'{{"shape":"circle","r":{c.radius}}}'
def visit_square(self, s): return f'{{"shape":"square","a":{s.side}}}'
shapes = [Circle(2), Square(3)]
print([s.accept(AreaVisitor()) for s in shapes])
print([s.accept(ExportVisitor()) for s in shapes])
编译器 AST 处理是访问者模式最经典的应用场景。
五、总结:如何选择
- 创建对象很麻烦 → 先想工厂方法/抽象工厂;参数太多用建造者;要复制已有对象用原型;全局唯一用单例。
- 想给对象加功能/控制访问 → 叠加职责用装饰器;接口不兼容用适配器;延迟加载、权限用代理;简化子系统用外观。
- 对象间在"打胶" → 一对多通知用观察者;请求处理链用责任链;撤销/重做用命令+备忘录;消灭 if/elif 分支用策略或状态。
- 慎用模式:模式是应对变化的工具,没有变化就别提前设计。过度设计比没有设计更可怕——"简单直接"往往才是最好的方案。

浙公网安备 33010602011771号