目录
设计模式-01 迭代器模式
简介
例子
类图
代码
分析特点
总结
通用结构
屏蔽实现
降低耦合
灵活迭代
Iterator 中文对应迭代器,用于在数据集合中按照顺序遍历集合。
举一个最基本的遍历数组的例子:
类中的 iterate 方法非常完美地解决了遍历的任务,小巧精干。
但是:
它的实现决定了它只能遍历数组。
暴露细节过多,如果要更改数据存储结构(arr 改成 List 类型了),则需要修改而不是添加代码,违反 OCP 原则。
这个方法告诉和保证给调用者的细节过多(数据存储类型),不符合面向抽象而非具体编程的原则。
……
为了解决这些问题,我们引入 Iterator 模式。
Aggregate 接口规定了它的实现类必须具有返回一个 Iterator 的方法,也就是说它的实现者能生成迭代器进行迭代。
Iterator 接口规定了它的实现类必须要有 next 和 hasNext 两个方法,也就是说,所有的迭代器必须要实现这两个功能。
BookShelfIterator 类实现了 Iterator 接口,表明它是一个迭代器,而且专门迭代 BookShelf。
BookShelf 类实现了 Aggregate 接口,所以它把自己作为参数,初始化返回了 BookShelfIterator 。
Client 要想遍历 BookShelf,只需要调用后者的 iterator 方法,获得 BookShef 的迭代器,然后使用迭代器进行迭代。不止对 BookShelf 这样进行迭代操作,用户对凡是实现了 Aggregate 接口的集合进行迭代,都是如此。在这个过程中:
迭代的过程和具体方式对 Client 来说是未知的,Client 用一个 Iterator 引用的实例进行迭代,非常符合面向抽象(接口)的原则。
假如 BookShelf 改用 <code>ArrayList<book></code>实现书本的存储,只需要增加一个新的、实现了 Iterator 接口的迭代器,并且在这个迭代器中编写遍历 ArrayList 的具体实现,最后在 BookShelf 的 iterator方法中返回新的迭代器即可,符合 OCP 原则。
Client 完全感知不到数据存储结构和遍历实现的改变,如果在新的迭代器实现中改成逆序迭代,Client 也只会感知到迭代出的元素顺序发生了改变,完全不清楚具体的细节。
这是更为一般的迭代器模式:
回到一个设计模式共性的问题:为什么这么麻烦?既然是一个数组,直接遍历处理就是最好的操作,似乎根本没有引入 Iterator 的必要。
核心问题:替换存储结构而完全不影响程序的其它部分。
如果 BookShelf 改用 java.util.List 管理书本,Client 使用 Iterator 进行迭代的操作代码不需要任何变动:
设计模式的作用之一就是帮助我们编写可复用的类,而所谓的可复用就是当一个组件(类)发生变化时,不需要对其他组件进行改动,或者是进行很小的改动就能应对新的变化和需求。这也进一步解释了,在 BookShelf 的 iterator 方法中返回的是一个 Iterator 而不是 BookShelfIterator。
以往,我们更喜欢使用具体的类来解决具体的问题,这是一种务实的做法,但具体类的编写往往会造成强的关联和耦合,设计模式的任务之一,就是要降低组件或者说代码之间的耦合。
一个 ConcreteAggregate 可以对应多个 ConcreteIterator,可以从前往后迭代,也可以从后往前迭代……