定义
迭代器模式(Iterator Pattern)是一种行为型设计模式,用于顺序访问集合对象的元素,而无需知道集合对象的底层表示。迭代器模式将遍历集合的责任从集合对象转移到迭代器对象上,这简化了集合接口和实现,同时也使得遍历不同的集合类型统一和标准化。
迭代器模式主要涉及以下角色:
- 迭代器(Iterator)接口:定义访问和遍历元素的接口。
- 具体迭代器(Concrete Iterator):实现迭代器接口,并负责遍历集合的具体对象。
- 集合(Aggregate)接口:定义创建迭代器对象的接口。
- 具体集合(Concrete Aggregate):实现创建迭代器对象的接口,返回一个适合该集合的具体迭代器实例。
- 客户端(Client):使用迭代器接口访问集合元素。
解决的问题
- 统一的遍历方式:提供一种统一的方法来遍历各种类型的集合对象,而不暴露其内部结构。
- 解耦集合对象和遍历逻辑:将集合的遍历逻辑从集合对象中分离出来,使得集合对象和遍历逻辑之间的职责更加清晰。
- 支持多种遍历:可以定义多种遍历方式,每种方式对应一个迭代器实现。
使用场景
- 不同的方式遍历集合:当集合对象需要提供多种遍历方式时,迭代器模式提供了一种灵活的解决方案。
- 访问集合的内容而无需暴露其内部结构:当需要提供一种标准的方式来遍历集合,而又不希望暴露集合的内部表示时。
- 允许在不同的集合类型上进行遍历:迭代器模式可以在不同类型的集合对象上实现统一的遍历接口。
示例代码
// 迭代器接口
public interface Iterator {boolean hasNext();Object next();
}// 集合接口
public interface Container {Iterator getIterator();
}// 具体集合实现
class NameRepository implements Container {public String names[] = {"Robert", "John", "Julie", "Lora"};@Overridepublic Iterator getIterator() {return new NameIterator();}// 内部类实现具体迭代器private class NameIterator implements Iterator {int index;@Overridepublic boolean hasNext() {return index < names.length;}@Overridepublic Object next() {if (this.hasNext()) {return names[index++];}return null;}}
}// 客户端使用迭代器
public class IteratorPatternDemo {public static void main(String[] args) {NameRepository namesRepository = new NameRepository();for (Iterator iter = namesRepository.getIterator(); iter.hasNext();) {String name = (String)iter.next();System.out.println("Name : " + name);}}
}
主要符合的设计原则
- 单一职责原则(Single Responsibility Principle):
- 迭代器模式将数据的遍历和业务逻辑分离。容器只负责管理元素,而迭代器负责遍历这些元素。这种分离确保了每个类都只有一个改变的原因,迭代器负责遍历逻辑,而容器类负责管理集合。
- 开闭原则(Open-Closed Principle):
- 该模式允许在不修改现有集合对象的情况下引入新的迭代器类型。例如,你可以引入一个新的迭代器来遍历容器中的元素,而无需修改容器类的代码。因此,容器类对扩展是开放的,但对修改是封闭的。
- 迪米特法则(Law of Demeter)或最少知识原则:
- 迭代器模式允许客户端代码仅与迭代器对象交互,而不需要直接处理集合内部的细节。这符合迪米特法则,即一个对象应该尽量少地了解其他对象。
- 里氏替换原则(Liskov Substitution Principle):
- 如果迭代器有一个基类或接口,那么其不同的实现(例如前向迭代器、后向迭代器或随机访问迭代器)可以互换使用,而不影响客户端代码的运行。这符合里氏替换原则,因为子类迭代器可以替换基类迭代器。
在JDK中的应用
- Java Collections Framework:
- 几乎所有的集合类(如
ArrayList
,HashSet
,LinkedList
等)都通过Iterator
接口提供了迭代器。这些集合类的iterator()
方法返回一个实现了Iterator
接口的对象,用于遍历集合中的元素。
- 几乎所有的集合类(如
- java.util.Iterator:
- 这是迭代器模式的核心接口。它定义了
next()
,hasNext()
, 和remove()
等方法,用于遍历集合并在必要时删除元素。
- 这是迭代器模式的核心接口。它定义了
- java.util.ListIterator:
ListIterator
是Iterator
接口的扩展,专门用于列表的双向遍历。除了标准的迭代器操作外,它还支持向前遍历、修改元素和获取元素的索引。
- java.util.Enumeration:
- 虽然现在已经不常用,
Enumeration
是早期Java版本中的一种迭代器,用于遍历例如Vector
和Hashtable
这样的数据结构。
- 虽然现在已经不常用,
- java.util.Scanner:
Scanner
类在某种程度上也实现了迭代器模式。它可以对输入进行解析,并以迭代的方式返回输入的各个部分(如通过next()
方法)。
- java.nio.file.DirectoryStream:
- 在NIO文件系统中,
DirectoryStream
接口用于遍历目录中的文件。它提供了一个迭代器来访问目录中的每个文件。
- 在NIO文件系统中,
在Spring中的应用
- Spring的资源处理:
- Spring提供了对资源的抽象,比如
Resource
接口和ResourceLoader
。在处理资源集合时,比如从一个目录加载所有配置文件,Spring可能内部使用迭代器模式来遍历这些资源。
- Spring提供了对资源的抽象,比如
- Bean的后处理器:
- 在Spring的应用上下文中,可能会有多个
BeanPostProcessor
实例。在初始化bean的过程中,Spring容器会遍历这些后处理器,并对bean应用它们。这种遍历的逻辑类似于迭代器模式。
- 在Spring的应用上下文中,可能会有多个
- 数据访问模块:
- 在Spring的数据访问模块,如JDBC或JPA集成中,处理查询结果集时可能会隐式使用迭代器模式。例如,
JdbcTemplate
可能使用迭代器来遍历ResultSet
。
- 在Spring的数据访问模块,如JDBC或JPA集成中,处理查询结果集时可能会隐式使用迭代器模式。例如,
- Spring Batch:
- 在Spring Batch框架中,对于批量数据处理,迭代器模式可能被用于逐条处理大量记录。
虽然这些例子并非迭代器模式的直接实现,但它们在设计上采用了迭代器模式的核心思想——通过迭代器来抽象和简化对集合或序列的访问。这种设计使得Spring的各个组件能够以统一和灵活的方式处理集合和序列数据,同时保持了代码的清晰性和可维护性。