迭代器模式:遍历聚合对象的统一接口
迭代器模式:遍历聚合对象的统一接口
摘要
你是否曾在代码中为了遍历不同的集合而写满各种for循环?迭代器模式提供了一种统一的方式来顺序访问聚合对象中的元素,无需暴露其底层表示。本教程带你从零掌握迭代器模式的核心思想、结构与实战应用。
1. 模式定义
迭代器模式(Iterator Pattern) 是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
简单说:把遍历行为从聚合对象中抽离出来,封装到一个独立的迭代器对象中。这样你可以使用相同的接口遍历列表、树、图等任何集合。
2. 为什么需要迭代器模式?
2.1 直面痛点
假设你有一个 List 和一个 BinaryTree,遍历它们的方式完全不同:
# 遍历列表
for i in range(len(my_list)):
print(my_list[i])
# 遍历二叉树?需要手动递归或栈操作,代码复杂且不统一
如果系统中存在多种集合,客户端代码会充斥着不同的遍历逻辑,导致:
- 高耦合:客户端需要知道每种集合的内部结构。
- 代码重复:遍历逻辑无法复用。
- 难以扩展:新增一种集合,所有用到遍历的地方都要修改。
2.2 模式带来的优势
- 统一接口:不管底层是数组、链表还是树,都通过
has_next()和next()访问元素。 - 隔离变化:集合改变内部表示,只要迭代器接口不变,客户端代码无需更改。
- 支持多种遍历:可以为同一个聚合定义多个迭代器(正序、倒序、DFS、BFS)。
- 简化聚合接口:聚合类不再需要包含遍历相关方法。
3. 模式结构
迭代器模式通常包含四个角色:
| 角色 | 说明 |
|---|---|
| 抽象迭代器(Iterator) | 定义访问和遍历元素的接口,如 first()、next()、has_next()、current_item() |
| 具体迭代器(ConcreteIterator) | 实现迭代器接口,记录遍历过程中的当前位置 |
| 抽象聚合(Aggregate) | 定义一个创建迭代器对象的接口 |
| 具体聚合(ConcreteAggregate) | 实现创建迭代器的接口,返回一个适合遍历该聚合的具体迭代器实例 |
UML 简图(文字描述):
Client 持有 Aggregate,通过 Aggregate.create_iterator() 获得 Iterator,然后使用 Iterator 方法遍历聚合内部元素。
4. 动手实现:一个通用集合迭代器
我们用 Python 实现一个简单的书架和借书清单迭代器。
4.1 抽象迭代器与聚合
from abc import ABC, abstractmethod
class Iterator(ABC):
@abstractmethod
def has_next(self) -> bool:
pass
@abstractmethod
def next(self):
pass
class Aggregate(ABC):
@abstractmethod
def create_iterator(self) -> Iterator:
pass
4.2 具体聚合:书架
书架内部使用 list 存储书籍,但外部只能通过迭代器访问。
class BookShelf(Aggregate):
def __init__(self):
self._books = []
def add_book(self, book: str):
self._books.append(book)
def __len__(self):
return len(self._books)
def _get_book_at(self, index: int):
return self._books[index]
def create_iterator(self) -> Iterator:
return BookShelfIterator(self)
4.3 具体迭代器
class BookShelfIterator(Iterator):
def __init__(self, book_shelf: BookShelf):
self._book_shelf = book_shelf
self._index = 0
def has_next(self) -> bool:
return self._index < len(self._book_shelf)
def next(self):
if not self.has_next():
raise StopIteration("没有更多书了")
book = self._book_shelf._get_book_at(self._index)
self._index += 1
return book
4.4 客户端使用
if __name__ == "__main__":
shelf = BookShelf()
shelf.add_book("《设计模式》")
shelf.add_book("《重构》")
shelf.add_book("《代码整洁之道》")
iterator = shelf.create_iterator()
while iterator.has_next():
print(iterator.next())
输出:
《设计模式》
《重构》
《代码整洁之道》
注意:客户端完全不依赖
_books列表的存在。未来如果BookShelf换成tuple或LinkedList,只要迭代器更新,客户端代码保持不变。
5. 再进一步:支持多种遍历策略
同一个聚合可以提供多个迭代器。例如,我们为书架增加一个倒序迭代器:
class ReverseBookShelfIterator(Iterator):
def __init__(self, book_shelf: BookShelf):
self._book_shelf = book_shelf
self._index = len(book_shelf) - 1
def has_next(self) -> bool:
return self._index >= 0
def next(self):
if not self.has_next():
raise StopIteration()
book = self._book_shelf._get_book_at(self._index)
self._index -= 1
return book
在 BookShelf 中添加新方法:
def create_reverse_iterator(self) -> Iterator:
return ReverseBookShelfIterator(self)
客户端可以自由选择迭代顺序,而书架本身不需要存放额外的游标信息。
6. 迭代器模式在现实中的身影
许多语言内置迭代器概念,你已经在不知不觉中使用了它:
- Python:
for item in collection:依赖对象的__iter__()和__next__()协议,本质就是迭代器模式。 - Java:
java.util.Iterator接口和foreach循环。 - C++:STL 迭代器是迭代器模式的经典实现,将算法与容器分离。
- 数据库游标:Cursor 就是一个迭代器,允许你逐行读取查询结果,而不关心底层存储结构。
7. 适用场景与注意事项
7.1 什么时候该用
- 需要为多种聚合对象提供统一的遍历方式。
- 需要隐藏集合的内部表示(数组、链表、红黑树等)。
- 需要支持对一个聚合对象的多种遍历方式(顺序、逆序、中序遍历等)。
- 希望对遍历过程进行控制和暂停(如使用
has_next判断边界,而非一次性全部取出)。
7.2 权衡利弊
| 优点 | 缺点 |
|---|---|
| 符合单一职责原则:将遍历行为与集合管理分离 | 对于简单遍历(如数组),使用迭代器可能增加代码复杂度 |
| 开闭原则:新增聚合和迭代器无需修改现有代码 | 某些情况下,直接使用语言内置的迭代器语法更简单 |
| 可以同时存在多个迭代器,各不干扰 | 若聚合频繁修改,需额外机制保证迭代器一致性(如 fail-fast) |
8. 高频面试问题速答
问:迭代器模式和普通 for 循环有什么区别?
答:普通循环直接依赖集合的底层结构(索引或指针),迭代器将遍历抽象出来,让客户端不用关心你用的是 list[0] 还是链表头结点。
问:如何实现一个可被 for-in 遍历的自定义类?
答:实现 __iter__() 返回一个迭代器对象,该对象需实现 __next__() 方法并在末尾抛出 StopIteration。
问:迭代器模式与组合模式有什么关系?
答:经常一起出现。组合模式产生树形结构,迭代器模式可以轻松实现深度优先或广度优先遍历整棵树。
9. 完整示例代码(一站式参考)
from abc import ABC, abstractmethod
# ---------- 抽象层 ----------
class Iterator(ABC):
@abstractmethod
def has_next(self): pass
@abstractmethod
def next(self): pass
class Aggregate(ABC):
@abstractmethod
def create_iterator(self): pass
# ---------- 具体聚合 ----------
class BookShelf(Aggregate):
def __init__(self):
self._books = []
def add_book(self, book):
self._books.append(book)
def __len__(self):
return len(self._books)
def _get_book_at(self, index):
return self._books[index]
def create_iterator(self):
return BookShelfIterator(self)
def create_reverse_iterator(self):
return ReverseBookShelfIterator(self)
# ---------- 具体迭代器 ----------
class BookShelfIterator(Iterator):
def __init__(self, shelf):
self._shelf = shelf
self._index = 0
def has_next(self):
return self._index < len(self._shelf)
def next(self):
if not self.has_next():
raise StopIteration
book = self._shelf._get_book_at(self._index)
self._index += 1
return book
class ReverseBookShelfIterator(Iterator):
def __init__(self, shelf):
self._shelf = shelf
self._index = len(shelf) - 1
def has_next(self):
return self._index >= 0
def next(self):
if not self.has_next():
raise StopIteration
book = self._shelf._get_book_at(self._index)
self._index -= 1
return book
# ---------- 客户端 ----------
if __name__ == "__main__":
shelf = BookShelf()
for title in ["A", "B", "C"]:
shelf.add_book(title)
print("正序:")
it = shelf.create_iterator()
while it.has_next():
print(it.next())
print("倒序:")
rev_it = shelf.create_reverse_iterator()
while rev_it.has_next():
print(rev_it.next())
10. 总结
迭代器模式的核心思想是将遍历与集合解耦。通过引入统一的迭代器接口,你将获得:
- 更清晰的代码结构(聚合专心管理数据,迭代器专注遍历)
- 易于切换底层数据结构的能力
- 支持多种遍历算法而无需修改已有代码
当你下次为“如何统一遍历不同类型的集合”而困扰时,迭代器模式就是那个久经考验的答案。
设计原则共鸣:单一职责原则(遍历职责分离)、开闭原则(扩展新的迭代器无需修改聚合)、依赖倒转原则(依赖抽象迭代器,而非具体集合)。
本教程仅供“免费在线教程”网站学习使用,欢迎分享、收藏。掌握迭代器,轻松遍历万物!