行为型模式关注对象之间的职责分配算法封装,描述多个对象如何协作完成单个对象无法独立完成的任务。


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)

行为型模式总结

模式核心思想主要解决的问题
观察者一对多依赖,自动通知更新状态变化需要通知多个对象
策略算法封装,可互相替换避免条件语句,运行时切换算法
模板方法定义算法骨架,步骤延迟到子类复用算法结构,允许子类定制步骤
命令请求封装为对象,支持撤销/队列解耦请求发送者和执行者
状态状态封装为对象,行为随状态改变大量状态相关条件语句
责任链请求沿链传递,直到被处理动态决定请求的接收者