各位同仁,大家好。今天我们汇聚一堂,将深入探讨一个在软件设计领域中至关重要的模式——迭代器模式(Iterator Pattern)。这个模式的核心思想在于:封装复杂数据结构遍历的统一接口。它不仅帮助我们优雅地解决数据遍历的难题,更是构建高内聚、低耦合系统不可或缺的工具。
在日常编程中,我们经常需要处理各种各样的数据集合:数组、列表、树、图,甚至是自定义的复杂结构。如何有效地遍历这些结构,从中取出我们所需的数据,同时又不对客户端代码暴露其内部实现细节,这正是迭代器模式所要解决的核心问题。
一、问题背景:为什么我们需要迭代器模式?
想象一下,你正在开发一个系统,其中包含多种数据存储方式。例如,你可能有一个 ProductList 对象,它内部使用一个数组 Product[] 来存储商品;另一个 OrderQueue 对象,它内部使用一个链表 LinkedList<Order> 来存储订单;甚至还有一个 CategoryTree 对象,它内部使用一个复杂的树形结构来管理商品分类。
现在,你的客户端代码需要遍历这些集合,并对其中的每个元素执行某种操作(例如,打印商品信息,处理订单,查找特定分类)。如果每次遍历时,客户端代码都需要了解并直接操作这些集合的内部结构,会带来一系列严重问题:
-
紧耦合(Tight Coupling):客户端代码与具体的数据结构实现紧密耦合。如果
ProductList将其内部存储从数组改为ArrayList,或者OrderQueue从链表改为双向队列,所有依赖这些内部实现的客户端代码都必须修改。这违反了开闭原则(Open/Closed Principle)。 -
代码重复(Code Duplication):不同的客户端代码可能需要以相同的方式遍历同一个集合。例如,一个报告生成模块和一个人机界面显示模块可能都需要遍历
ProductList并获取所有商品。如果每次都从头编写遍历逻辑,会导致大量重复代码。 -
职责不清(Violation of SRP):集合对象本身(例如
ProductList)应该专注于管理和存储数据,而不是提供多种遍历算法。将遍历逻辑直接内置到集合中,会使集合的职责变得过于庞大,违反了单一职责原则(Single Responsibility Principle)。 -
难以扩展(Poor Extensibility):如果需要为同一个集合添加新的遍历方式(例如,正序遍历、逆序遍历、按特定条件过滤遍历),就需要在集合类中添加更多方法,或者在客户端代码中编写复杂的逻辑。这使得系统难以维护和扩展。
为了解决这些问题,我们需要一种机制,能够将数据结构的遍历行为从数据结构本身中抽离出来,并提供一个统一的接口供客户端使用。这就是迭代器模式的用武之地。
二、迭代器模式的核心概念与参与者
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法来顺序访问聚合对象中各个元素,而无需暴露该对象的内部表示。
该模式主要包含以下几个核心参与者:
-
Iterator(迭代器接口):
- 定义访问和遍历元素的接口。
- 通常包含
hasNext()(判断是否还有下一个元素)、next()(返回下一个元素并移动游标)等方法。 - 可选地,可以包含
remove()方法,用于从集合中删除当前元素。
-
ConcreteIterator(具体迭代器):
- 实现
Iterator接口。 - 负责维护遍历的当前位置。
- 知道如何遍历它所关联的特定聚合对象。
- 实现
-
Aggregate(聚合接口):
- 定义创建相应迭代器对象的接口。
- 通常包含一个
createIterator()方法,返回一个Iterator接口的实例。
-
ConcreteAggregate(具体聚合):
- 实现
Aggregate接口。 - 负责存储数据。
- 其
createIterator()方法会返回一个与其内部数据结构相匹配的ConcreteIterator实例。
- 实现
通过这种方式,客户端代码只需要与 Iterator 和 Aggregate 接口打交道,完全不需要知道 ConcreteAggregate 的内部实现细节,从而实现了高度解耦。
我们用一张表格来概括这些参与者及其职责:
| 参与者 | 职责 | 典型方法 |
|---|---|---|
| Iterator | 定义访问和遍历元素的接口。 | hasNext(), next(), remove() (可选) |
| ConcreteIterator | 实现 Iterator 接口,维护遍历状态,知道如何遍历特定聚合。 | hasNext(), next(), remove() |
| Aggregate | 定义创建迭代器对象的接口。 | createIterator() |
| ConcreteAggregate | 实现 Aggregate 接口,存储数据,返回 ConcreteIterator 实例。 | createIterator() (返回与其内部结构匹配的迭代器) |
三、一个简单的例子:数组列表的迭代
为了更好地理解迭代器模式,我们首先从一个相对简单的例子开始:一个自定义的商品列表。
假设我们有一个 Product 类:
// Product.java
public class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + '}';
}
}
现在,我们来定义迭代器接口和聚合接口。
// MyIterator.java
public interface MyIterator<T> {
boolean hasNext();
T next();
// void remove(); // 可选的删除操作
}
// MyAggregate.java
public interface MyAggregate<T> {
MyIterator<T> createIterator();
}
接下来,我们实现一个具体的聚合 ProductList,它内部使用一个数组来存储 Product 对象,并实现 MyAggregate 接口。同时,我们还需要一个 ProductListIterator 来实现 MyIterator 接口。
// ProductList.java (ConcreteAggregate)
public class ProductList implements MyAggregate<Product> {
private Product[] products;
private int numberOfProducts;
private static final int MAX_PRODUCTS = 100;
public ProductList() {
products = new Product[MAX_PRODUCTS];
numberOfProducts = 0;
}
public void addProduct(Product product) {
if (numberOfProducts < MAX_PRODUCTS) {
products[numberOfProducts] = product;
numberOfProducts++;
} else {
System.out.println("Product list is full. Cannot add more products.");
}
}
public Product getProduct(int index) {
if (index >= 0 && index < numberOfProducts) {
return products[index];
}
return null;
}
public int getNumberOfProducts() {
return numberOfProducts;
}
@Override
public MyIterator<Product> createIterator() {
return new ProductListIterator(this);
}
// ProductListIterator.java (ConcreteIterator)
private class ProductListIterator implements MyIterator<Product> {
private ProductList productList;
private int position = 0;
public ProductListIterator(ProductList productList) {
this.productList = productList;
}
@Override
public boolean hasNext() {
// 注意:这里需要使用外部类的 numberOfProducts
return position < productList.getNumberOfProducts();
}
@Override
public Product next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
// 注意:这里需要使用外部类的 getProduct 方法
Product product = productList.getProduct(position);
position++;
return product;
}
}
}
现在,我们来看看客户端代码如何使用这个迭代器:
// Client.java
public class Client {
public static void main(String[] args) {
ProductList productList = new ProductList();
productList.addProduct(new Product("Laptop", 1200.00));
productList.addProduct(new Product("Mouse", 25.00));
productList.addProduct(new Product("Keyboard", 75.00));
System.out.println("--- Iterating through ProductList ---");
MyIterator<Product> iterator = productList.createIterator();
while (iterator.hasNext()) {
Product product = iterator.next();
System.out.println(product);
}
// 假设我们有另一个聚合,例如一个订单队列,内部用链表实现
// OrderQueue orderQueue = new OrderQueue();
// orderQueue.addOrder(new Order("ORD001", ...));
// MyIterator<Order> orderIterator = orderQueue.createIterator();
// while(orderIterator.hasNext()) { ... }
// 客户端代码只需要关心 MyIterator 接口,而无需关心 OrderQueue 的内部实现
}
}
在这个例子中,客户端代码只通过 MyIterator 接口与 ProductList 进行交互。它不需要知道 ProductList 内部是用数组实现的,也不需要知道如何通过索引来访问数组元素。这种解耦极大地提高了系统的灵活性和可维护性。
四、深入探索:链表和树的迭代器
上述数组的例子相对简单,其遍历逻辑直接基于索引。迭代器模式的真正威力体现在处理更复杂的数据结构时,例如链表和树。
4.1 链表的迭代器
让我们考虑一个单向链表 MyLinkedList。
// Node.java
class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
this.next = null;
}
}
// MyLinkedList.java (ConcreteAggregate)
public class MyLinkedList<T> implements MyAggregate<T> {
private Node<T> head;
private int size;
public MyLinkedList() {
this.head = null;
this.size = 0;
}
public void add(T data) {
Node<T> newNode = new Node<>(data);
if (head == null) {
head = newNode;
} else {
Node<T> current = head;
while (current.next != null) {
current = current.next;
}
current.next = newNode;
}
size++;
}
public int size() {
return size;
}
// MyLinkedListIterator.java (ConcreteIterator) - 内部类更方便访问 head
@Override
public MyIterator<T> createIterator() {
return new MyLinkedListIterator();
}
private class MyLinkedListIterator implements MyIterator<T> {
private Node<T> current;
public MyLinkedListIterator() {
this.current = MyLinkedList.this.head; // 访问外部类的head
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
T data = current.data;
current = current.next;
return data;
}
}
}
客户端使用方式与 ProductList 相同:
// Client for LinkedList
public class LinkedListClient {
public static void main(String[] args) {
MyLinkedList<String> names = new MyLinkedList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
System.out.println("n--- Iterating through MyLinkedList ---");
MyIterator<String> nameIterator = names.createIterator();
while (nameIterator.hasNext()) {
System.out.println("Name: " + nameIterator.next());
}
}
}
在这里,MyLinkedListIterator 封装了链表的遍历逻辑,即通过 current.next 来前进。客户端完全感知不到 Node 对象的存在,它只知道通过 hasNext() 和 next() 来获取下一个元素。
4.2 树的迭代器:多种遍历策略
树形结构提供了更复杂的遍历场景,因为存在多种标准的遍历方式(前序、中序、后序、层序等)。迭代器模式在这里展现出其强大的灵活性:同一个树形聚合可以提供不同类型的迭代器,每种迭代器实现一种特定的遍历算法。
我们以二叉树的中序遍历为例。
// TreeNode.java
class TreeNode<T> {
T data;
TreeNode<T> left;
TreeNode<T> right;
public TreeNode(T data) {
this.data = data;
this.left = null;
this.right = null;
}
}
// BinaryTree.java (ConcreteAggregate)
public class BinaryTree<T> implements MyAggregate<T> {
private TreeNode<T> root;
public BinaryTree(TreeNode<T> root) {
this.root = root;
}
// 假设我们有一个方法来构建树,或者直接在构造函数中传入根节点
// ...
@Override
public MyIterator<T> createIterator() {
// 默认提供中序遍历迭代器
return new InOrderIterator(this.root);
}
// 如果需要,可以提供其他遍历方式的迭代器创建方法
public MyIterator<T> createPreOrderIterator() {
return new PreOrderIterator(this.root);
}
// InOrderIterator.java (ConcreteIterator)
private class InOrderIterator implements MyIterator<T> {
private java.util.Stack<TreeNode<T>> stack;
private TreeNode<T> current;
public InOrderIterator(TreeNode<T> root) {
stack = new java.util.Stack<>();
current = root;
// 预加载左侧节点到栈中
pushAllLeft(current);
}
private void pushAllLeft(TreeNode<T> node) {
while (node != null) {
stack.push(node);
node = node.left;
}
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public T next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
TreeNode<T> node = stack.pop();
// 处理右子树
pushAllLeft(node.right);
return node.data;
}
}
// PreOrderIterator.java (另一个 ConcreteIterator)
private class PreOrderIterator implements MyIterator<T> {
private java.util.Stack<TreeNode<T>> stack;
public PreOrderIterator(TreeNode<T> root) {
stack = new java.util.Stack<>();
if (root != null) {
stack.push(root);
}
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public T next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
TreeNode<T> node = stack.pop();
// 先压入右子节点,再压入左子节点,这样出栈时就是左、右的顺序(前序遍历是根-左-右)
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
return node.data;
}
}
}
客户端使用不同的迭代器:
// Client for BinaryTree
public class BinaryTreeClient {
public static void main(String[] args) {
// 构建一棵简单的二叉树
// 4
// /
// 2 5
// /
// 1 3
TreeNode<Integer> root = new TreeNode<>(4);
root.left = new TreeNode<>(2);
root.right = new TreeNode<>(5);
root.left.left = new TreeNode<>(1);
root.left.right = new TreeNode<>(3);
BinaryTree<Integer> tree = new BinaryTree<>(root);
System.out.println("n--- In-Order Traversal (Expected: 1 2 3 4 5) ---");
MyIterator<Integer> inOrderIterator = tree.createIterator(); // 默认中序
while (inOrderIterator.hasNext()) {
System.out.print(inOrderIterator.next() + " ");
}
System.out.println();
System.out.println("n--- Pre-Order Traversal (Expected: 4 2 1 3 5) ---");
MyIterator<Integer> preOrderIterator = tree.createPreOrderIterator();
while (preOrderIterator.hasNext()) {
System.out.print(preOrderIterator.next() + " ");
}
System.out.println();
}
}
在这个例子中,BinaryTree 聚合提供了两种不同的迭代器(InOrderIterator 和 PreOrderIterator),它们各自实现了不同的遍历算法。客户端可以根据需要选择使用哪个迭代器,而无需关心这些复杂遍历逻辑的实现细节。这完美地体现了迭代器模式的灵活性和对开放/封闭原则的支持。
五、迭代器模式的优势与局限
5.1 优势
- 实现客户端与聚合的解耦:客户端代码只需要依赖
Iterator和Aggregate接口,而无需了解具体聚合对象的内部结构和遍历实现。这使得聚合的内部表示可以自由改变,而不会影响到客户端代码。 - 支持多种遍历方式:同一个聚合对象可以提供多种不同的迭代器,每种迭代器实现一种特定的遍历算法(如正序、逆序、按条件过滤、深度优先、广度优先等)。
- 简化聚合对象的职责:聚合对象只负责管理数据和提供创建迭代器的方法,遍历逻辑被委托给迭代器对象,遵循了单一职责原则。
- 支持并发遍历:多个迭代器可以独立地遍历同一个聚合对象,每个迭代器维护自己的遍历状态,互不干扰。这对于多线程环境或需要同时进行不同遍历的应用场景非常有用。
- 提高代码的可重用性:一旦实现了通用的迭代器接口,客户端代码就可以用统一的方式处理不同的聚合对象,增强了代码的通用性和可重用性。
- 易于扩展:添加新的遍历算法只需实现一个新的
ConcreteIterator类,而无需修改现有的聚合类或客户端代码,符合开闭原则。
5.2 局限性
- 增加类的数量和复杂性:对于非常简单的聚合结构(例如,一个普通数组),引入迭代器模式可能会增加额外的接口和类的数量,使得代码结构略显复杂。但对于复杂的数据结构,这种复杂性是值得的。
- 性能开销(微乎其微):创建迭代器对象和通过接口调用方法可能存在微小的性能开销。但在大多数实际应用中,这种开销可以忽略不计。
- 处理聚合修改的挑战:如果在迭代器遍历过程中,聚合对象被修改(例如,添加或删除了元素),可能会导致迭代器失效,产生
ConcurrentModificationException等问题。这通常需要特殊的处理策略(如“快速失败”机制、写时复制等),增加了实现的复杂性。
六、内部迭代器与外部迭代器
迭代器模式通常指的是外部迭代器(External Iterator),即客户端代码显式地控制遍历过程,通过反复调用 hasNext() 和 next() 方法来驱动迭代。我们前面所有的例子都属于外部迭代器。
// 外部迭代器示例
MyIterator<Product> iterator = productList.createIterator();
while (iterator.hasNext()) { // 客户端控制何时检查下一个
Product product = iterator.next(); // 客户端控制何时获取下一个
// ... 对 product 进行操作
}
与外部迭代器相对的是内部迭代器(Internal Iterator)。内部迭代器将遍历逻辑封装在迭代器内部,客户端只需提供一个在每个元素上执行的操作(通常是一个回调函数或Lambda表达式),迭代器会负责调用该操作遍历所有元素。
许多现代编程语言提供了内部迭代器的语法糖,例如 Java 8 的 forEach 方法、Python 的 for ... in ... 循环(当与 map 或 filter 等高阶函数结合时,可以看作是内部迭代器的一种体现)以及 JavaScript 的 forEach。
以 Java 为例:
// Java 8 内部迭代器示例
// 假设 ProductList 实现了 Iterable 接口,并提供了 forEach 方法
productList.forEach(product -> {
System.out.println(product); // 客户端提供操作,迭代器(forEach内部)控制遍历
});
内部迭代器的优点是代码更简洁,意图更明确,但缺点是灵活性较低,客户端无法在遍历过程中中断或进行复杂的控制。外部迭代器则提供了更高的灵活性,允许客户端在遍历的每一步进行自定义决策。
七、迭代器模式与语言特性
迭代器模式是如此普遍和有用,以至于许多现代编程语言都将其作为核心语言特性或标准库的一部分。
7.1 Java
Java 提供了 java.util.Iterator<E> 接口和 java.lang.Iterable<T> 接口。
Iterator<E>:定义了hasNext()、next()和remove()方法。Iterable<T>:定义了iterator()方法,返回一个Iterator<T>实例。
任何实现了 Iterable 接口的类都可以被 Java 的增强型 for 循环(for-each 循环)遍历。
// Java 的标准 Iterator 和 Iterable 接口
public interface Iterable<T> {
Iterator<T> iterator(); // 返回一个迭代器
}
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() { // 默认实现,通常抛出 UnsupportedOperationException
throw new UnsupportedOperationException("remove");
}
// Java 8 引入的 forEachRemaining 方法
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
让我们将之前的 ProductList 改造为符合 Java 标准的 Iterable:
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ProductList implements Iterable<Product> {
private Product[] products;
private int numberOfProducts;
private static final int MAX_PRODUCTS = 100;
public ProductList() {
products = new Product[MAX_PRODUCTS];
numberOfProducts = 0;
}
public void addProduct(Product product) {
if (numberOfProducts < MAX_PRODUCTS) {
products[numberOfProducts] = product;
numberOfProducts++;
} else {
System.out.println("Product list is full. Cannot add more products.");
}
}
public int getNumberOfProducts() {
return numberOfProducts;
}
// 实现 Iterable 接口的 iterator() 方法
@Override
public Iterator<Product> iterator() {
return new ProductListIterator();
}
// ProductListIterator 作为内部类实现 java.util.Iterator 接口
private class ProductListIterator implements Iterator<Product> {
private int position = 0;
@Override
public boolean hasNext() {
return position < numberOfProducts; // 直接访问外部类的成员
}
@Override
public Product next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Product product = products[position]; // 直接访问外部类的成员
position++;
return product;
}
// 可以选择实现 remove 方法
// @Override
// public void remove() {
// throw new UnsupportedOperationException("Remove not supported by this iterator.");
// }
}
}
客户端代码现在可以非常简洁地使用 for-each 循环:
// Java Client with for-each
public class JavaClient {
public static void main(String[] args) {
ProductList productList = new ProductList();
productList.addProduct(new Product("Laptop", 1200.00));
productList.addProduct(new Product("Mouse", 25.00));
productList.addProduct(new Product("Keyboard", 75.00));
System.out.println("--- Iterating through ProductList with for-each ---");
for (Product product : productList) { // 强大的语法糖
System.out.println(product);
}
// 也可以使用传统的 while 循环
System.out.println("n--- Iterating through ProductList with explicit Iterator ---");
Iterator<Product> it = productList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
7.2 Python
Python 的迭代器模式是语言的核心特性之一。任何实现了 __iter__() 和 __next__() 方法的对象都可以被视为迭代器,并可以使用 for ... in ... 循环遍历。
__iter__(self):返回迭代器对象本身。__next__(self):返回下一个元素。如果没有更多元素,则应抛出StopIteration异常。
通常,一个可迭代对象(Iterable)是定义了 __iter__() 方法(该方法返回一个迭代器)的对象。而迭代器(Iterator)是定义了 __iter__() 和 __next__() 方法的对象。
# Python 示例:ProductList 和迭代器
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __str__(self):
return f"Product{{name='{self.name}', price={self.price}}}"
class ProductList: # 可迭代对象 (Iterable)
def __init__(self):
self._products = []
def add_product(self, product):
self._products.append(product)
def __iter__(self):
# 返回一个迭代器实例
return ProductListIterator(self._products)
class ProductListIterator: # 迭代器 (Iterator)
def __init__(self, products):
self._products = products
self._index = 0
def __iter__(self):
return self # 迭代器本身也是可迭代的,返回自身
def __next__(self):
if self._index < len(self._products):
product = self._products[self._products[self._index]
self._index += 1
return product
else:
raise StopIteration # 没有更多元素时抛出 StopIteration 异常
Python 客户端代码:
# Python 客户端
if __name__ == "__main__":
product_list = ProductList()
product_list.add_product(Product("Laptop", 1200.00))
product_list.add_product(Product("Mouse", 25.00))
product_list.add_product(Product("Keyboard", 75.00))
print("--- Iterating through ProductList with for-in loop ---")
for product in product_list:
print(product)
# 也可以手动获取迭代器
print("n--- Iterating through ProductList with explicit iterator ---")
it = iter(product_list) # 调用 product_list.__iter__()
try:
while True:
product = next(it) # 调用 it.__next__()
print(product)
except StopIteration:
pass
Python 的生成器(Generator)和 yield 关键字提供了一种更简洁的方式来创建迭代器,尤其是当遍历逻辑比较复杂时:
# Python 生成器示例
class ProductListWithGenerator:
def __init__(self):
self._products = []
def add_product(self, product):
self._products.append(product)
def __iter__(self):
# 使用 yield 关键字,这是一个生成器函数
for product in self._products:
yield product
# 客户端使用方式相同
if __name__ == "__main__":
product_list_gen = ProductListWithGenerator()
product_list_gen.add_product(Product("Monitor", 300.00))
product_list_gen.add_product(Product("Webcam", 50.00))
print("n--- Iterating through ProductListWithGenerator ---")
for product in product_list_gen:
print(product)
生成器在需要按需生成序列元素时非常有用,它能有效节省内存,因为它不会一次性将所有元素加载到内存中。
7.3 C
C# 也提供了类似的机制,通过 IEnumerable<T> 和 IEnumerator<T> 接口,以及 foreach 循环和 yield return 关键字。
IEnumerable<T>:定义了GetEnumerator()方法,返回一个IEnumerator<T>实例。IEnumerator<T>:定义了Current属性、MoveNext()方法和Reset()方法。
// C# 示例
using System;
using System.Collections;
using System.Collections.Generic;
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
public Product(string name, double price)
{
Name = name;
Price = price;
}
public override string ToString()
{
return $"Product{{name='{Name}', price={Price}}}";
}
}
// ProductList 实现 IEnumerable<Product>
public class ProductList : IEnumerable<Product>
{
private List<Product> _products;
public ProductList()
{
_products = new List<Product>();
}
public void AddProduct(Product product)
{
_products.Add(product);
}
// 实现 IEnumerable<Product> 接口的 GetEnumerator 方法
public IEnumerator<Product> GetEnumerator()
{
// 可以直接使用 List 的迭代器
// return _products.GetEnumerator();
// 或者实现自定义迭代器
return new ProductListIterator(this);
}
// 显式实现非泛型 IEnumerable 接口,以兼容旧版代码
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
// 自定义迭代器类
private class ProductListIterator : IEnumerator<Product>
{
private ProductList _productList;
private int _position = -1; // 初始位置在第一个元素之前
public ProductListIterator(ProductList productList)
{
_productList = productList;
}
public Product Current
{
get
{
if (_position < 0 || _position >= _productList._products.Count)
{
throw new InvalidOperationException();
}
return _productList._products[_position];
}
}
object IEnumerator.Current => Current; // 非泛型接口的实现
public bool MoveNext()
{
_position++;
return (_position < _productList._products.Count);
}
public void Reset()
{
_position = -1;
}
public void Dispose()
{
// 资源清理,如果需要
}
}
}
// C# 客户端
public class Client
{
public static void Main(string[] args)
{
ProductList productList = new ProductList();
productList.AddProduct(new Product("Laptop", 1200.00));
productList.AddProduct(new Product("Mouse", 25.00));
productList.AddProduct(new Product("Keyboard", 75.00));
Console.WriteLine("--- Iterating through ProductList with foreach ---");
foreach (var product in productList)
{
Console.WriteLine(product);
}
Console.WriteLine("n--- Iterating through ProductList with explicit IEnumerator ---");
using (IEnumerator<Product> enumerator = productList.GetEnumerator())
{
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
}
}
}
C# 的 yield return 关键字也提供了一种非常简洁的方式来创建迭代器:
// C# yield return 示例
public class ProductListWithYield : IEnumerable<Product>
{
private List<Product> _products;
public ProductListWithYield()
{
_products = new List<Product>();
}
public void AddProduct(Product product)
{
_products.Add(product);
}
public IEnumerator<Product> GetEnumerator()
{
foreach (var product in _products)
{
yield return product; // 编译器会自动生成迭代器逻辑
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
// 客户端使用方式相同
public class YieldClient
{
public static void Main(string[] args)
{
ProductListWithYield productList = new ProductListWithYield();
productList.AddProduct(new Product("Monitor", 300.00));
productList.AddProduct(new Product("Webcam", 50.00));
Console.WriteLine("n--- Iterating through ProductListWithYield ---");
foreach (var product in productList)
{
Console.WriteLine(product);
}
}
}
这些语言内置的迭代器支持极大地简化了迭代器模式的实现和使用,让开发者能够专注于业务逻辑,而不是重复编写遍历代码。
八、何时使用迭代器模式
当以下情况出现时,应该考虑使用迭代器模式:
- 你需要访问一个聚合对象的内容,而不需要暴露其内部表示。
- 你需要支持对同一个聚合对象进行多种遍历。例如,正向遍历、反向遍历、跳跃遍历、特定条件过滤遍历等。
- 你需要提供一个统一的接口来遍历不同的聚合结构(例如,数组、链表、树),以便客户端代码可以以相同的方式处理它们。
- 聚合对象的遍历逻辑复杂,如果将其直接放在聚合对象中会使其职责过重。
- 需要支持并发遍历,即多个客户端可以独立地、同时地遍历同一个聚合对象。
九、迭代器模式的考量与变体
- 删除操作:
Iterator接口通常包含一个remove()方法。实现这个方法需要谨慎,因为它会修改底层聚合。如果不支持删除,应该抛出UnsupportedOperationException。在并发环境中,删除操作可能导致迭代器失效,需要特殊的并发控制机制。 - 快照与实时迭代:迭代器可以实现为“快照”迭代器,即在创建时复制聚合的当前状态,然后遍历这个快照。这样,即使原始聚合在遍历过程中被修改,迭代器也能正常工作,但会消耗额外内存。另一种是“实时”迭代器,它直接访问原始聚合。这种迭代器在聚合被修改时可能失效(如 Java 的
ConcurrentModificationException机制),但能节省内存。 - 空迭代器:当聚合为空时,
createIterator()方法可以返回一个“空迭代器”(Null Iterator),它总是返回false作为hasNext(),并且在调用next()时抛出异常。这简化了客户端代码,因为它不需要检查聚合是否为空。
十、总结与展望
迭代器模式是软件设计中一个强大而基础的工具。它通过引入迭代器对象,将数据结构的遍历职责从数据结构本身中剥离出来,从而实现了客户端代码与数据结构实现的彻底解耦。这不仅提高了系统的模块化程度和可维护性,也为数据遍历提供了极大的灵活性和可扩展性。
从简单的列表到复杂的树形结构,迭代器模式都能提供统一且优雅的遍历解决方案。现代编程语言对迭代器模式的内置支持,如 Java 的 Iterable 和 Iterator、Python 的 __iter__ 和 __next__,以及 C# 的 IEnumerable 和 IEnumerator,使得这一模式在日常开发中无处不在,成为处理集合数据的标准范式。掌握迭代器模式,对于编写高质量、可维护、可扩展的代码至关重要。