在日常开发中,迭代器模式(Iterator Pattern)是我们时时刻刻都在用,但却存在感最小的一种设计模式。对于普通的 Java 业务开发者来说,我们基本上不会去单独写一个迭代器模式,这是因为大部分常用的容器都已经提供了迭代器模式的实现了,如 List、Set 等等。
即便是自定义的容器,我们也不会去自己写迭代器的实现,而是用 JDK 中提供的迭代器接口——Iterator。今天就以 JDK 中提供的迭代器模式最佳实践 Iterator 接口来介绍该模式。
1. 定义
迭代器模式是一种行为型设计模式,它的定义是:“它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。”
定义中描述了迭代器模式解决的问题:提供遍历容器元素的方法。同时还描述了使用迭代器模式提供此方法的优势:不需要暴露容器的内部细节。
什么叫不需要暴露容器的内部细节?暴露容器内部细节有什么问题吗?
如果遍历容器需要暴露容器的内部细节,则意味在开发者在对容器元素进行删除时,由于容器整体元素减1,还需要将当前遍历元素的索引数向前移动一位,这样才不会在后面的遍历过程中发生类似于数组越界的异常。
而如果将容器内部细节隐藏,则开发者在对容器进行增删操作的时候,并不需要关心当前遍历元素索引的位置,降低了复杂度。
2. 类图
在迭代器模式中,有这样几种角色:
- 抽象聚合角色(Aggregate):定义容器的增删方法以及创建迭代器的方法
- 具体聚合角色(ConcreteAggregate):抽象聚合接口的实现类
- 抽象迭代器角色(Iterator):定义遍历容器的方法,如 next()、hasNext() 方法
- 具体迭代器角色(Concretelterator):抽象迭代器接口的实现类,实现对容器的遍历方法
迭代器模式的类图如下:
3. 示例
迭代器模式的最佳实践,JDK 已经做的非常好了,下面我们就来新建一个用户优惠券的容器类实现 JDK 提供的 Iterator 接口来实现用户优惠券的快速遍历。
首先看一下 JDK 提供的 Iterator 接口源码:
public interface Iterator<E> {
// 判断是否有下一个元素
boolean hasNext();
// 返回下一个元素
E next();
// 默认不支持删除
default void remove() {
throw new UnsupportedOperationException("remove");
}
// 省略 forEachRemaining 方法
}
然后定义容器中的元素优惠券类以及持有优惠券列表的用户,其代码如下:
public class Coupon {
private String name;
public Coupon(String name) {
this.name = name;
}
// 省略 setter/getter 方法
}
public class UserCoupon {
private List<Coupon> list = new ArrayList<>();
public void add(Coupon coupon) {
list.add(coupon);
}
public void remove(Coupon coupon) {
list.remove(coupon);
}
public Iterator<Coupon> getIterator() {
return list.iterator();
}
}
然后编写测试代码:
public class Test {
public static void main(String[] args) {
UserCoupon userCoupon = new UserCoupon();
userCoupon.add(new Coupon("满100-10券"));
userCoupon.add(new Coupon("满200-20券"));
userCoupon.add(new Coupon("满300-30券"));
Iterator<Coupon> iterator = userCoupon.getIterator();
while (iterator.hasNext()) {
Coupon next = iterator.next();
System.out.println(next.getName());
}
}
}
输出结果为:
满100-10券
满200-20券
满300-30券
可能上面的示例有点画蛇添足多此一举,但是返回迭代器能直接用 iterator.next() 是因为 List 接口原本就实现了迭代器的功能,否则实现迭代器模式还需要定义很多其他逻辑。
有兴趣的同学可以自己去看一看 ArrayList 的代码中关于迭代器的部分,写得还是比较清楚的。
4. 使用场景
迭代器模式的使用场景是:
- 访问一个聚合对象的内容且无须暴露其内部细节
- 需要为访问聚合对象提供多种不同的遍历方法时,可以使用迭代器模式对这些遍历方法进行统一抽象
5. 小结
本文讲述了迭代器模式,它的定义是——它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
迭代器模式的优点是:
- 访问一个聚合对象的内容且无须暴露其内部细节
- 访问聚合对象功能由迭代器实现,与聚合类解耦
- 拓展性良好,新增访问方法只需要新增迭代器子类即可
迭代器模式的缺点是:
- 如果存在很多迭代方法,一定程序上增加了系统的复杂度
6. 参考资料
- 《设计模式之禅》(第2版)
- 迭代器模式(详解版)
最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。
文档信息
- 本文作者:Planeswalker23
- 本文链接:https://planeswalker23.github.io/2022/02/08/%E6%88%91%E6%89%80%E7%90%86%E8%A7%A3%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%B3%BB%E5%88%97-%E7%AC%AC17%E7%AF%87-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%E5%AE%9E%E7%8E%B0%E5%BF%AB%E9%80%9F%E9%81%8D%E5%8E%86/
- 版权声明:本作品系原创,作者保留所有权利,未经作者允许,禁止转载和演绎。