行为型模式关注对象之间的职责分配和算法封装,描述多个对象如何协作完成单个对象无法独立完成的任务。
1. 观察者模式(Observer)
意图
定义对象间的一对多依赖关系,当一个对象的状态改变时,所有依赖它的对象都得到通知并自动更新。
问题
一个对象(Subject)的状态变化需要通知多个其他对象(Observers),但 Subject 不应与 Observer 紧密耦合。
解决方案
- Subject 提供注册、移除、通知观察者的接口。
- Observer 定义一个更新接口。
- Subject 状态改变时,遍历注册的观察者,调用其更新方法。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Subject │ │ Observer │
├─────────────┤ ├─────────────┤
│ + attach(Observer) │ │ + update() │
│ + detach(Observer) │ └─────────────┘
│ + notify() │ △
└─────────────┘ ┌─────────────┐
△ │ ConcreteObserver│
┌─────────────┐ ├─────────────┤
│ConcreteSubject│◀────────│ - subjectState│
│ - state │ │ + update() │
└─────────────┘ └─────────────┘
参与者
- Subject:知道它的观察者,提供注册和删除接口。
- ConcreteSubject:存储状态,状态改变时调用 notify。
- Observer:定义更新接口。
- ConcreteObserver:维护对 ConcreteSubject 的引用,实现 update 方法。
协作
- 客户端创建 Subject 和 Observer,然后将 Observer 注册到 Subject。
- 当 Subject 状态变化,自动通知所有 Observer,Observer 拉取或接收新状态。
适用性
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
- 对一个对象的改变需要同时改变其他对象,但不知道有多少对象需要改变。
- 对象应能在不假设其他对象身份的情况下通知其他对象。
Python 示例
from abc import ABC, abstractmethod
# Observer
class Observer(ABC):
@abstractmethod
def update(self, temperature: float): pass
# Subject
class WeatherStation:
def __init__(self):
self._observers = []
self._temperature = 0.0
def attach(self, observer: Observer):
self._observers.append(observer)
def detach(self, observer: Observer):
self._observers.remove(observer)
def notify(self):
for obs in self._observers:
obs.update(self._temperature)
def set_temperature(self, temp: float):
self._temperature = temp
self.notify()
# Concrete Observers
class PhoneDisplay(Observer):
def update(self, temperature: float):
print(f"Phone: Current temperature is {temperature}°C")
class WindowDisplay(Observer):
def update(self, temperature: float):
print(f"Window: Temperature updated to {temperature}°C")
# Client
station = WeatherStation()
phone = PhoneDisplay()
window = WindowDisplay()
station.attach(phone)
station.attach(window)
station.set_temperature(25.0)
station.set_temperature(30.5)2. 策略模式(Strategy)
意图
定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化。
问题
一个类有多个变体行为(如不同排序算法、不同支付方式),如果通过条件语句硬编码,则难以扩展和维护。
解决方案
- 定义策略接口(Strategy),声明算法所需的方法。
- 具体策略类实现该接口,封装具体算法。
- 上下文(Context) 类持有一个策略引用,并将算法调用委托给策略对象。
- 客户端可以在运行时选择具体策略。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Context │ │ Strategy │
├─────────────┤ ├─────────────┤
│ - strategy │─────────▶│ + algorithm()│
│ + setStrategy(s)│ └─────────────┘
│ + execute() │ △
└─────────────┘ ┌─────────────┐
│ConcreteStrategyA│
├─────────────┤
│ + algorithm()│
└─────────────┘
参与者
- Strategy:定义所有支持的算法的公共接口。
- ConcreteStrategy:实现 Strategy 接口的具体算法。
- Context:维护对 Strategy 对象的引用,可定义接口让策略访问其数据。
协作
- 客户端创建具体策略对象,并传递给 Context。
- Context 在执行操作时调用策略对象的算法方法。
适用性
- 许多相关类仅行为不同,策略模式提供一种用多个行为之一配置类的方法。
- 需要算法的不同变体。
- 算法使用客户端不应知道的数据,使用策略模式避免暴露复杂的数据结构。
Python 示例
from abc import ABC, abstractmethod
# Strategy
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: float): pass
# Concrete Strategies
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number: str):
self.card_number = card_number
def pay(self, amount: float):
print(f"Paid {amount} using Credit Card {self.card_number[-4:]}")
class PayPalPayment(PaymentStrategy):
def __init__(self, email: str):
self.email = email
def pay(self, amount: float):
print(f"Paid {amount} using PayPal account {self.email}")
# Context
class ShoppingCart:
def __init__(self):
self._items = []
self._payment_strategy = None
def add_item(self, price: float):
self._items.append(price)
def set_payment_strategy(self, strategy: PaymentStrategy):
self._payment_strategy = strategy
def checkout(self):
total = sum(self._items)
if self._payment_strategy is None:
raise ValueError("No payment strategy set")
self._payment_strategy.pay(total)
# Client
cart = ShoppingCart()
cart.add_item(10.5)
cart.add_item(20.0)
cart.set_payment_strategy(CreditCardPayment("1234-5678-9012-3456"))
cart.checkout()
cart.set_payment_strategy(PayPalPayment("user@example.com"))
cart.checkout()3. 模板方法模式(Template Method)
意图
定义一个操作中的算法的骨架,将一些步骤延迟到子类中。模板方法使子类可以不改变算法结构即可重新定义算法的某些步骤。
问题
多个类有相似的算法流程,但某些步骤的实现不同。如果直接复制代码,重复度高且难以维护。
解决方案
- 父类定义模板方法,其中包含算法的基本步骤(基本方法),并调用抽象或钩子方法。
- 子类实现抽象方法或覆盖钩子方法,从而改变算法的特定步骤。
结构(UML)
┌─────────────┐
│ AbstractClass │
├─────────────┤
│ + templateMethod() │
│ # primitiveOp1() │
│ # primitiveOp2() │
│ # hook() (可选) │
└─────────────┘
△
┌─────────────┐
│ ConcreteClass │
├─────────────┤
│ # primitiveOp1()│
│ # primitiveOp2()│
└─────────────┘
参与者
- AbstractClass:定义模板方法和基本操作接口。
- ConcreteClass:实现基本操作以完成算法中与特定子类相关的步骤。
协作
- 客户端调用模板方法,模板方法调用基本方法(由子类实现)和钩子方法。
适用性
- 一次性实现算法的不变部分,并将可变的行为留给子类实现。
- 子类公共行为应提取到父类,避免代码重复。
- 控制子类扩展,定义钩子方法让子类在特定点扩展。
Python 示例
from abc import ABC, abstractmethod
# AbstractClass
class DataMiner(ABC):
def mine(self): # template method
self.open_file()
data = self.extract_data()
parsed_data = self.parse_data(data)
self.analyze(parsed_data)
self.close_file()
self.generate_report()
def open_file(self): print("Opening file...")
def close_file(self): print("Closing file...")
def generate_report(self): print("Generating report...")
@abstractmethod
def extract_data(self): pass
@abstractmethod
def parse_data(self, data): pass
def analyze(self, data): # hook (可选的)
print("Default analysis")
# ConcreteClass
class PDFMiner(DataMiner):
def extract_data(self):
print("Extracting data from PDF")
return "PDF raw data"
def parse_data(self, data):
print(f"Parsing PDF: {data}")
return ["parsed", "data"]
class CSVMiner(DataMiner):
def extract_data(self):
print("Reading CSV file")
return "CSV raw data"
def parse_data(self, data):
print(f"Parsing CSV: {data}")
return ["csv", "rows"]
# Client
PDFMiner().mine()
CSVMiner().mine()4. 命令模式(Command)
意图
将请求封装为对象,从而使用户可用不同的请求、队列或日志请求来参数化其他对象,并支持可撤销操作。
问题
需要将请求的发送者与执行者解耦,或需要支持请求的排队、日志、撤销等功能。
解决方案
- Command 接口声明执行方法(如
execute())。 - ConcreteCommand 实现 execute,内部调用接收者(Receiver)的动作。
- Invoker 持有命令对象,并触发命令。
- Receiver 知道如何执行具体操作。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Invoker │ │ Command │
├─────────────┤ ├─────────────┤
│ + setCommand(c)│ │ + execute() │
│ + invoke() │────▶└─────────────┘
└─────────────┘ △
┌─────────────┐
│ConcreteCommand│
├─────────────┤
│ - receiver │
│ + execute() │
└─────────────┘
│
┌─────────────┐
│ Receiver │
├─────────────┤
│ + action() │
└─────────────┘
参与者
- Command:声明执行操作的接口。
- ConcreteCommand:将操作绑定到接收者,调用接收者的相应操作。
- Invoker:调用命令对象执行请求。
- Receiver:知道如何执行具体业务逻辑。
协作
- 客户端创建 ConcreteCommand 并设置其 Receiver。
- Invoker 存储命令,并在适当时机调用命令的 execute。
适用性
- 需要将请求参数化对象(如菜单项)。
- 需要支持请求的排队、日志或撤销。
- 需要支持宏命令(组合命令)。
Python 示例
# Receiver
class Light:
def on(self): print("Light is ON")
def off(self): print("Light is OFF")
# Command interface
class Command:
def execute(self): pass
def undo(self): pass
# ConcreteCommands
class LightOnCommand(Command):
def __init__(self, light: Light):
self._light = light
def execute(self):
self._light.on()
def undo(self):
self._light.off()
class LightOffCommand(Command):
def __init__(self, light: Light):
self._light = light
def execute(self):
self._light.off()
def undo(self):
self._light.on()
# Invoker
class RemoteControl:
def __init__(self):
self._history = []
def set_command(self, command: Command):
self._command = command
def press_button(self):
if hasattr(self, '_command'):
self._command.execute()
self._history.append(self._command)
def undo(self):
if self._history:
last = self._history.pop()
last.undo()
# Client
light = Light()
on_cmd = LightOnCommand(light)
off_cmd = LightOffCommand(light)
remote = RemoteControl()
remote.set_command(on_cmd)
remote.press_button()
remote.set_command(off_cmd)
remote.press_button()
remote.undo() # undo last command (off -> on)5. 状态模式(State)
意图
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
问题
对象的行为依赖于它的状态,并且在运行时根据状态改变行为。如果使用大量条件语句,代码臃肿且难以维护。
解决方案
- Context 类持有一个 State 引用,将请求委托给当前状态对象。
- State 接口定义所有状态下的行为。
- ConcreteState 实现特定状态的行为,并可决定状态转换。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Context │ │ State │
├─────────────┤ ├─────────────┤
│ - state │─────▶│ + handle() │
│ + request() │ └─────────────┘
└─────────────┘ △
┌─────────────┐
│ConcreteStateA│
├─────────────┤
│ + handle() │
└─────────────┘
参与者
- Context:定义客户端感兴趣的接口,维护 State 实例。
- State:定义封装特定状态行为的接口。
- ConcreteState:实现 State 接口,可自行决定是否切换 Context 的状态。
协作
- Context 将请求委托给当前 State 对象处理。
- State 对象可改变 Context 的 state 引用,实现状态转换。
适用性
- 对象的行为取决于其状态,且运行时要根据状态改变行为。
- 操作中含有庞大的多分支条件语句,且这些分支依赖于对象的状态。
Python 示例
from abc import ABC, abstractmethod
# State
class State(ABC):
@abstractmethod
def handle(self, context): pass
# ConcreteStates
class ConcreteStateA(State):
def handle(self, context):
print("State A handling request. Switching to State B.")
context.set_state(ConcreteStateB())
class ConcreteStateB(State):
def handle(self, context):
print("State B handling request. Switching to State A.")
context.set_state(ConcreteStateA())
# Context
class Context:
def __init__(self, state: State):
self._state = state
def set_state(self, state: State):
self._state = state
def request(self):
self._state.handle(self)
# Client
context = Context(ConcreteStateA())
context.request()
context.request()
context.request()6. 责任链模式(Chain of Responsibility)
意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。将这些对象连成一条链,并沿着链传递请求,直到有一个对象处理它为止。
问题
请求的发送者需要知道具体哪个接收者处理,但接收者可能动态变化,或需要多个接收者之一处理。
解决方案
- 定义处理器(Handler) 接口,包含设置后继者(successor)的方法和处理请求的方法。
- 每个具体处理器决定自己能否处理请求;如果不能,则转发给后继者。
结构(UML)
┌─────────────┐
│ Handler │
├─────────────┤
│ + setSuccessor(h)│
│ + handleRequest()│
└─────────────┘
△
┌─────────────┐
│ConcreteHandler│
├─────────────┤
│ + handleRequest()│
└─────────────┘
参与者
- Handler:定义处理请求的接口,并可选择实现后继链。
- ConcreteHandler:处理它负责的请求,否则转发给后继。
协作
- 客户端将请求发送到链上的某个处理器,处理器沿着链传递。
适用性
- 多个对象可以处理同一请求,但具体由哪个处理在运行时决定。
- 希望在不明确指定接收者的情况下向多个对象中的一个提交请求。
- 处理器的集合应动态指定。
Python 示例
from abc import ABC, abstractmethod
# Handler
class SupportHandler(ABC):
def __init__(self):
self._next = None
def set_next(self, handler):
self._next = handler
return handler
@abstractmethod
def handle(self, request): pass
# ConcreteHandlers
class Level1Support(SupportHandler):
def handle(self, request):
if request == "basic":
print("Level1: Handling basic request")
elif self._next:
self._next.handle(request)
else:
print("Request cannot be handled")
class Level2Support(SupportHandler):
def handle(self, request):
if request == "advanced":
print("Level2: Handling advanced request")
elif self._next:
self._next.handle(request)
else:
print("Request cannot be handled")
class Level3Support(SupportHandler):
def handle(self, request):
if request == "expert":
print("Level3: Handling expert request")
elif self._next:
self._next.handle(request)
else:
print("Request cannot be handled")
# Client
l1 = Level1Support()
l2 = Level2Support()
l3 = Level3Support()
l1.set_next(l2).set_next(l3)
for req in ["basic", "advanced", "expert", "unknown"]:
print(f"\nRequest: {req}")
l1.handle(req)行为型模式总结
| 模式 | 核心思想 | 主要解决的问题 |
|---|---|---|
| 观察者 | 一对多依赖,自动通知更新 | 状态变化需要通知多个对象 |
| 策略 | 算法封装,可互相替换 | 避免条件语句,运行时切换算法 |
| 模板方法 | 定义算法骨架,步骤延迟到子类 | 复用算法结构,允许子类定制步骤 |
| 命令 | 请求封装为对象,支持撤销/队列 | 解耦请求发送者和执行者 |
| 状态 | 状态封装为对象,行为随状态改变 | 大量状态相关条件语句 |
| 责任链 | 请求沿链传递,直到被处理 | 动态决定请求的接收者 |