涵盖 GoF 经典模式中未提及的剩余行为型模式(迭代器、中介者、备忘录、访问者、解释器),以及一个常用模式(简单工厂)。采用抽象语言为主,Python 示例为辅。
title:Info
**说明**:以下模式在实际项目中频率相对较低,但特定场景下非常有用。理解它们有助于完善设计模式知识体系。1. 迭代器模式(Iterator)
意图
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
问题
聚合对象(如列表、树)的内部结构可能不同,但客户端希望以统一的方式遍历元素。直接在聚合类中实现遍历会暴露内部结构,且难以支持多种遍历方式。
解决方案
- 将遍历行为提取到独立的迭代器对象中。
- 迭代器接口定义
first()、next()、is_done()、current_item()等方法。 - 聚合对象实现
create_iterator()方法,返回对应的迭代器。 - 不同的迭代器可以提供前向、后向、深度优先等不同遍历策略。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Aggregate │ │ Iterator │
├─────────────┤ ├─────────────┤
│ + createIterator()│────▶│ + first() │
└─────────────┘ │ + next() │
△ │ + isDone() │
┌─────────────┐ │ + currentItem()│
│ConcreteAggregate│ └─────────────┘
└─────────────┘ △
┌─────────────┐
│ConcreteIterator│
└─────────────┘
参与者
- Iterator:定义访问和遍历元素的接口。
- ConcreteIterator:实现 Iterator 接口,记录当前位置。
- Aggregate:定义创建迭代器对象的接口。
- ConcreteAggregate:实现创建迭代器的方法,返回 ConcreteIterator。
协作
- 客户端通过 Aggregate 获取迭代器,然后使用迭代器遍历元素,无需知道聚合内部结构。
适用性
- 需要访问聚合对象内容而不暴露其内部表示。
- 需要支持对聚合对象的多种遍历方式。
- 需要为不同的聚合结构提供统一的遍历接口。
Python 示例
from collections.abc import Iterator, Iterable
# 利用 Python 内置协议实现
class MyCollection(Iterable):
def __init__(self, data):
self._data = data
def __iter__(self):
return MyIterator(self._data)
class MyIterator(Iterator):
def __init__(self, data):
self._data = data
self._index = 0
def __next__(self):
if self._index >= len(self._data):
raise StopIteration
value = self._data[self._index]
self._index += 1
return value
# 手动实现标准迭代器模式(非 Pythonic)
class Book:
def __init__(self, title): self.title = title
class BookShelf:
def __init__(self): self._books = []
def add_book(self, book): self._books.append(book)
def create_iterator(self): return BookShelfIterator(self)
class BookShelfIterator:
def __init__(self, shelf):
self._shelf = shelf
self._index = 0
def has_next(self): return self._index < len(self._shelf._books)
def next(self):
book = self._shelf._books[self._index]
self._index += 1
return book
# Client
shelf = BookShelf()
shelf.add_book(Book("Design Patterns"))
shelf.add_book(Book("Clean Code"))
it = shelf.create_iterator()
while it.has_next():
print(it.next().title)2. 中介者模式(Mediator)
意图
用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,并且可以独立地改变它们之间的交互。
问题
多个对象相互引用形成网状结构,导致对象之间紧密耦合,难以复用和修改。例如:对话框中的控件(按钮、文本框、下拉框)相互影响。
解决方案
- 引入中介者对象,将多对多的交互转化为中介者与同事对象之间的一对多交互。
- 同事对象(Colleague)只与中介者通信,不直接与其他同事通信。
- 中介者知道所有同事,并协调它们的请求。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Mediator │ │ Colleague │
├─────────────┤ ├─────────────┤
│ + notify(sender, event)│◀────│ + send(event)│
└─────────────┘ │ + receive(event)│
△ └─────────────┘
┌─────────────┐ △
│ConcreteMediator│ ┌─────────────┐
│ - colleagues │──────│ConcreteColleague│
└─────────────┘ └─────────────┘
参与者
- Mediator:声明通信接口。
- ConcreteMediator:实现协调逻辑,知道所有同事。
- Colleague:每个同事类在状态改变时通知中介者,由中介者转发给其他同事。
协作
- 同事对象不直接引用其他同事,它们通过中介者发送和接收请求。
适用性
- 一组对象以定义良好但复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用很多其他对象并与之通信,导致难以复用。
- 想定制一个分布在多个类中的行为,而又不想生成太多子类。
Python 示例
# Mediator
class ChatRoomMediator:
def show_message(self, user, message): pass
# ConcreteMediator
class ChatRoom(ChatRoomMediator):
def show_message(self, user, message):
print(f"[{user.name}]: {message}")
# Colleague
class User:
def __init__(self, name, mediator: ChatRoomMediator):
self.name = name
self._mediator = mediator
def send(self, message):
self._mediator.show_message(self, message)
# Client
room = ChatRoom()
alice = User("Alice", room)
bob = User("Bob", room)
alice.send("Hello")
bob.send("Hi Alice")3. 备忘录模式(Memento)
意图
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
问题
需要保存对象的状态(如撤销操作),但又不想暴露对象的内部实现细节。
解决方案
- 发起人(Originator) 创建包含其当前内部状态的备忘录对象,并可使用备忘录恢复状态。
- 备忘录(Memento) 存储发起人的内部状态,但对外部不公开任何细节(仅发起人可访问)。
- 管理者(Caretaker) 负责保存备忘录,但不能修改或检查备忘录内容。
结构(UML)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Originator │ │ Memento │ │ Caretaker │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ + createMemento()│──▶│ - state │──────│ + save(m) │
│ + restore(m)│ └─────────────┘ │ + undo() │
└─────────────┘ └─────────────┘
参与者
- Originator:创建备忘录以保存当前状态,并使用备忘录恢复状态。
- Memento:存储 Originator 的内部状态。除了 Originator,不允许其他对象访问。
- Caretaker:负责保存备忘录,但不能对备忘录内容操作。
协作
- Caretaker 向 Originator 请求备忘录,保存一段时间后,将其传回 Originator 以恢复状态。
适用性
- 需要保存对象状态以便后续恢复,但又不能直接暴露对象接口。
- 需要实现撤销/重做功能。
Python 示例
# Memento (简单使用字典或数据类)
class EditorMemento:
def __init__(self, content):
self._content = content
def get_content(self):
return self._content
# Originator
class Editor:
def __init__(self):
self._content = ""
def type(self, words):
self._content += words
def get_content(self):
return self._content
def save(self):
return EditorMemento(self._content)
def restore(self, memento: EditorMemento):
self._content = memento.get_content()
# Caretaker
class History:
def __init__(self):
self._states = []
def push(self, memento):
self._states.append(memento)
def pop(self):
if self._states:
return self._states.pop()
return None
# Client
editor = Editor()
history = History()
editor.type("Hello, ")
history.push(editor.save())
editor.type("world!")
history.push(editor.save())
editor.type(" How are you?")
print(editor.get_content()) # Hello, world! How are you?
editor.restore(history.pop())
print(editor.get_content()) # Hello, world!
editor.restore(history.pop())
print(editor.get_content()) # Hello,4. 访问者模式(Visitor)
意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
问题
需要对一个由固定种类对象组成的结构(如抽象语法树)添加新的操作,但避免修改每个对象类。
解决方案
- 访问者(Visitor) 接口为每个具体元素类声明一个访问方法(如
visitConcreteElementA)。 - 元素(Element) 接口定义
accept(visitor)方法。 - 具体元素实现
accept,调用访问者的相应访问方法,并将自身作为参数传递。 - 客户端可以创建不同的 Visitor 对象,并让元素接受访问,从而执行不同操作。
结构(UML)
┌─────────────┐ ┌─────────────┐
│ Visitor │ │ Element │
├─────────────┤ ├─────────────┤
│ + visitConcreteA(e)│◀──│ + accept(v)│
│ + visitConcreteB(e)│ └─────────────┘
└─────────────┘ △
△ ┌─────────────┐┌─────────────┐
┌─────────────┐ │ConcreteElementA││ConcreteElementB│
│ConcreteVisitor│ └─────────────┘└─────────────┘
└─────────────┘
参与者
- Visitor:为每个 ConcreteElement 声明一个 visit 操作。
- ConcreteVisitor:实现每个 visit 操作,定义特定操作。
- Element:定义 accept 操作,以 Visitor 为参数。
- ConcreteElement:实现 accept,调用 Visitor 对应的方法。
协作
- 客户端遍历对象结构,对每个元素调用
accept(visitor)。 - 元素将自身传递给 Visitor 的相应方法。
适用性
- 对象结构包含许多具有不同接口的类,需要对这些类执行一些不相关的操作。
- 对象结构很少变化,但经常需要在此结构上定义新操作。
- 类层次结构稳定,但操作易变。
Python 示例
# Visitor
class ShapeVisitor:
def visit_circle(self, circle): pass
def visit_rectangle(self, rectangle): pass
# Element
class Shape:
def accept(self, visitor: ShapeVisitor): pass
# Concrete Elements
class Circle(Shape):
def __init__(self, radius): self.radius = radius
def accept(self, visitor):
visitor.visit_circle(self)
class Rectangle(Shape):
def __init__(self, w, h): self.w, self.h = w, h
def accept(self, visitor):
visitor.visit_rectangle(self)
# Concrete Visitors
class AreaCalculator(ShapeVisitor):
def visit_circle(self, circle):
area = 3.14 * circle.radius ** 2
print(f"Circle area: {area}")
def visit_rectangle(self, rect):
area = rect.w * rect.h
print(f"Rectangle area: {area}")
class Drawer(ShapeVisitor):
def visit_circle(self, circle):
print(f"Drawing circle with radius {circle.radius}")
def visit_rectangle(self, rect):
print(f"Drawing rectangle {rect.w}x{rect.h}")
# Client
shapes = [Circle(5), Rectangle(4, 6)]
area_calc = AreaCalculator()
drawer = Drawer()
for shape in shapes:
shape.accept(area_calc)
shape.accept(drawer)5. 解释器模式(Interpreter)
意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
问题
需要解释执行一种简单的语言(如正则表达式、数学表达式、配置文件),且语法规则相对稳定。
解决方案
- 为文法中的每个规则定义一个表达式类(如 TerminalExpression、NonterminalExpression)。
- 表达式类实现
interpret(context)方法。 - 客户端构建抽象语法树(AST),然后调用解释器。
结构(UML)
┌─────────────┐
│ Context │
└─────────────┘
│
┌─────────────┐
│ Expression │
├─────────────┤
│ + interpret(context)│
└─────────────┘
△
┌─────────────┐ ┌─────────────┐
│TerminalExpression│ │NonterminalExpression│
└─────────────┘ └─────────────┘
参与者
- AbstractExpression:声明解释接口。
- TerminalExpression:实现文法中的终结符表达式。
- NonterminalExpression:实现非终结符表达式,通常包含子表达式。
- Context:包含解释器之外的全局信息。
协作
- 客户端构建AST,调用根表达式的 interpret 方法。
适用性
- 文法简单,效率不是主要考虑因素。
- 需要解释执行的语言可以表示为抽象语法树。
Python 示例
# Context
class Context:
def __init__(self, text):
self._tokens = text.split()
self._index = 0
def next_token(self):
if self._index < len(self._tokens):
token = self._tokens[self._index]
self._index += 1
return token
return None
# AbstractExpression
class Expression:
def interpret(self, context): pass
# TerminalExpression
class NumberExpression(Expression):
def __init__(self, value):
self.value = int(value)
def interpret(self, context):
return self.value
# NonterminalExpression
class AddExpression(Expression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) + self.right.interpret(context)
# 简易解析器(演示)
class Parser:
@staticmethod
def parse(context):
left = NumberExpression(context.next_token())
op = context.next_token()
right = NumberExpression(context.next_token())
if op == "+":
return AddExpression(left, right)
raise ValueError("Unknown operator")
# Client
ctx = Context("3 + 5")
expr = Parser.parse(ctx)
result = expr.interpret(ctx)
print(result) # 86. 简单工厂模式(Simple Factory)
—— 非GoF,但常用
意图
提供一个创建对象的接口,根据传入的参数决定实例化哪个具体类。
问题
客户端需要根据条件创建不同的对象,但直接使用 if-else 或 switch 会与具体类耦合。
解决方案
- 创建一个工厂类,其中包含一个静态方法,根据参数返回不同的产品实例。
- 客户端仅依赖工厂类,不依赖具体产品类。
结构(UML)
┌─────────────┐ ┌─────────────┐
│SimpleFactory│ │ Product │
├─────────────┤ ├─────────────┤
│ + createProduct(type)│──▶│ (interface) │
└─────────────┘ └─────────────┘
△
┌─────────────┐
│ConcreteProductA/B│
└─────────────┘
参与者
- SimpleFactory:负责创建产品,根据参数决定具体类型。
- Product:产品接口。
- ConcreteProduct:具体产品实现。
协作
- 客户端调用工厂的静态方法,传入类型标识,获取产品实例。
适用性
- 产品类型较少且很少变化。
- 客户端无需知道产品的实例化细节。
Python 示例
from abc import ABC, abstractmethod
# Product
class Animal(ABC):
@abstractmethod
def speak(self): pass
class Dog(Animal):
def speak(self): return "Woof"
class Cat(Animal):
def speak(self): return "Meow"
# Simple Factory
class AnimalFactory:
@staticmethod
def create(animal_type: str) -> Animal:
if animal_type == "dog":
return Dog()
elif animal_type == "cat":
return Cat()
else:
raise ValueError(f"Unknown animal type {animal_type}")
# Client
animal = AnimalFactory.create("dog")
print(animal.speak())总结表
| 模式 | 类型(GoF) | 核心思想 |
|---|---|---|
| 迭代器 | 行为型 | 分离遍历逻辑与聚合结构 |
| 中介者 | 行为型 | 封装对象间交互,降低耦合 |
| 备忘录 | 行为型 | 保存和恢复对象状态,不破坏封装 |
| 访问者 | 行为型 | 在不改变类结构的前提下添加新操作 |
| 解释器 | 行为型 | 为简单语言定义文法并解释执行 |
| 简单工厂 | 创建型(非GoF) | 单一工厂类根据参数返回产品 |