我所理解的设计模式系列·第17篇·自定义容器如何基于迭代器模式实现快速遍历

2022/02/08 设计模式 共 2220 字,约 7 分钟

在日常开发中,迭代器模式(Iterator Pattern)是我们时时刻刻都在用,但却存在感最小的一种设计模式。对于普通的 Java 业务开发者来说,我们基本上不会去单独写一个迭代器模式,这是因为大部分常用的容器都已经提供了迭代器模式的实现了,如 List、Set 等等。

即便是自定义的容器,我们也不会去自己写迭代器的实现,而是用 JDK 中提供的迭代器接口——Iterator。今天就以 JDK 中提供的迭代器模式最佳实践 Iterator 接口来介绍该模式。

design-patttern-17-迭代器模式-封面

1. 定义

迭代器模式是一种行为型设计模式,它的定义是:“它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

定义中描述了迭代器模式解决的问题:提供遍历容器元素的方法。同时还描述了使用迭代器模式提供此方法的优势:不需要暴露容器的内部细节。

什么叫不需要暴露容器的内部细节?暴露容器内部细节有什么问题吗?

如果遍历容器需要暴露容器的内部细节,则意味在开发者在对容器元素进行删除时,由于容器整体元素减1,还需要将当前遍历元素的索引数向前移动一位,这样才不会在后面的遍历过程中发生类似于数组越界的异常。

而如果将容器内部细节隐藏,则开发者在对容器进行增删操作的时候,并不需要关心当前遍历元素索引的位置,降低了复杂度。

2. 类图

在迭代器模式中,有这样几种角色:

  • 抽象聚合角色(Aggregate):定义容器的增删方法以及创建迭代器的方法
  • 具体聚合角色(ConcreteAggregate):抽象聚合接口的实现类
  • 抽象迭代器角色(Iterator):定义遍历容器的方法,如 next()、hasNext() 方法
  • 具体迭代器角色(Concretelterator):抽象迭代器接口的实现类,实现对容器的遍历方法

迭代器模式的类图如下:

design-patttern-17-迭代器模式-1-类图

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. 参考资料

最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。

文档信息

Search

    Table of Contents