14.2 解 决 方 案

14.2.1 使用迭代器模式来解决问题

用来解决上述问题的一个合理的解决方案就是迭代器模式。那么什么是迭代器模式呢?

1.迭代器模式的定义

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。

所谓聚合是指一组对象的组合结构,比如:Java中的集合、数组等。

2.是应用迭代器模式来解决问题的思路

仔细分析上面的问题,要以一个统一的方式来访问内部实现不同的聚合对象,那么首先需要把这个统一的访问方式定义出来,按照这个统一的访问方式定义出来的接口,在迭代器模式中对应的就是Iterator接口。

迭代器迭代的是具体的聚合对象,那么不同的聚合对象就应该有不同的迭代器,为了让迭代器以一个统一的方式来操作聚合对象,因此给所有的聚合对象抽象出一个公共的父类,让它提供操作聚合对象的公共接口,这个抽象的公共父类在迭代器模式中对应的就是Aggregate对象。

接下来就该考虑如何创建迭代器了。由于迭代器和相应的聚合对象紧密相关,因此让具体的聚合对象来负责创建相应的迭代器对象。

14.2.2 迭代器模式的结构和说明

迭代器模式的结构如图14.1所示。

图片

图14.1 迭代器模式的结构示意图

■ Iterator:迭代器接口。定义访问和遍历元素的接口。

■ ConcreteIterator:具体的迭代器实现对象。实现对聚合对象的遍历,并跟踪遍历时的当前位置。

■ Aggregate:聚合对象。定义创建相应迭代器对象的接口。

■ ConcreteAggregate:具体聚合对象。实现创建相应的迭代器对象。

14.2.3 迭代器模式示例代码

(1)先来看看迭代器接口的定义。示例代码如下:

9c1fdd5149cf45548398a013bd671740

424de8750f454e9f9f6c8a979136d0f8

(2)接下来看看具体的迭代器实现示意。示例代码如下:

41ddea4ff6b84fb78adb521228e6b967

4ce65a0c1086489cb686c306052955b4

(3)再来看看聚合对象的定义。示例代码如下:

2164ebe80a9a45b1bb2d21ae63bd8a39

(4)下面来看看具体的聚合对象的实现,这里示意的是数组。示例代码如下:

ce45dda4851644e3aadde7e400f2a6f6

60c375965dbc43218ddc94cee56b651b

3325ea4cd8cd40908793efb6660c4e60

(5)最后来看看如何使用这个聚合对象和迭代器对象。示例代码如下:

5649001c217348f0a71ef9fb5221e332

be6971abf9ec441999232048ea6c2cec

14.2.4 使用迭代器模式来实现示例

要使用迭代器模式来实现示例,先来看看已有的两个工资系统现在的情况,然后再根据前面学习的迭代器模式来改造。

1.已有的系统

(1)首先是有一个已经统一了的工资描述模型。为了演示简单,这里只留下最基本的字段,描述一下支付工资的人员、支付的工资数额,其他的包括时间等都不描述了;同时为了后面调试方便,实现了toString方法。示例代码如下:

9ae459b4d38d49b290d863b2c447778a

b29eb2c41ca740ffaf8bb0d7e2876ff0

(2)客户方已有的工资管理系统中的工资管理类,内部是通过List来管理的。简单的示例代码如下:

ad68fcc0d9f04b4aaea9c792a56c841b

3fdf967a60444c898f2ae5d7ef20ac6b

(3)客户方收购的那家公司的工资管理系统中的工资管理类,内部是通过数组来管理的。简单的示例代码如下:

9698e3171f3e47e288a256931919d27d

a2c83af343f14ddaa9fbe433b11b8156

(4)如果此时从外部来访问这两个工资列表,外部要采用不同的访问方式:一个是访问数组,另一个是访问集合对象。示例代码如下:

aa4f5ab1e4554c868635389c4cbba772

仔细查看框住的代码,会发现它们的访问方式是完全不一样的。

运行结果如下:

2db69da071dc4de088660c13ce71b1cc

2.统一访问聚合的接口

要使用迭代器模式来整合访问上面两个聚合对象,那就需要先定义出抽象的聚合对象和迭代器接口来,然后再提供相应的实现。

使用迭代器模式实现示例的结构如图14.2所示。

图片

图14.2 使用迭代器模式实现示例的结构示意图

(1)为了让客户端能够以一个统一的方式进行访问,最容易的方式就是为它们定义一个统一的接口,通过统一的接口来访问。这个示例用的Iterator和模式的示例代码是一样的,这里就不注释了。示例代码如下:

228c392f301441e78af02ce2abb71b65

(2)定义好了统一的接口,那就得分别实现这个接口。一个是List实现的,另一个是数组实现的,先来看数组实现的访问。示例代码如下:

9b13f96e1003454b93857f96dcbb1bba

52e2ab5883744c2f9ad1fe0aa868fdfe

为了让客户端能以统一的方式访问数据,所以对集合也提供一个对接口Iterator的实现。示例代码如下:

d4c056419fd74127aa37952a9e93e7d9

01991033666f48beb49ab9519b535472

b1fb3f925f33403b94b96d390aa8d62b

(3)获取访问聚合的接口。

定义好了统一的访问聚合的接口,也分别实现了这个接口,新的问题是,在客户端如何才能获取这个访问聚合的接口呢?而且还要以统一的方式来获取。

一个简单的方案就是定义一个获取访问聚合的接口的接口,客户端先通过这个接口来获取访问聚合的接口,然后再访问聚合对象。示例代码如下:

ff77799c7a21423aa57dc837ebdeb567

然后让具体的聚合对象PayManager和SalaryManager来继承这个抽象类,提供分别访问它们的访问聚合的接口。

修改PayManager对象,添加createIterator方法的实现,另外再添加迭代器回调聚合对象的方法,一个方法是获取聚合对象的大小,另一个方法是根据索引获取聚合对象中的元素。示例代码如下:

1d5fb1e0342c4b5f99b9a054cb4479ca

同理修改SalaryManager对象。示例代码如下:

324810e1f71e4bfeb58c698c8843aca6

40c6a807ecfd4d4e824022e831e0e2b4

(4)统一访问的客户端。

下面就来看看客户端是如何通过迭代器接口来访问聚合对象的。为了显示是统一的访问,干脆把通过访问聚合的接口来访问聚合对象的功能独立成一个方法。虽然是访问不同的聚合对象,但是都调用这个方法去访问。示例代码如下:

be154a27acac4a45ac1cc04fe91d3dd1

73c6cb1e642642129e8e8c9358c07d8d

运行一下客户端,测试看看效果。

提示

估计有些朋友看到这里,会觉得上面的实现特麻烦,会认为“Java里面就有Iterator接口,而且Java集合框架中的聚合对象也大都实现了Iterator接口的功能,还有必要像上面这么做吗?”

其实这么做,是为了让大家看到迭代器模式的全貌,后面会讲到用Java中的迭代器来实现。另外,有些时候还是需要自己来扩展和实现迭代器模式的,所以还是应该先独立学习迭代器模式。

(5)迭代器示例小结。

如同前面的示例,提供了一个统一访问聚合对象的接口,通过这个接口就可以顺序地访问聚合对象的元素。对于客户端而言,只是面向这个接口在访问,根本不知道聚合对象内部的表示方法。

事实上,前面的例子故意做了一个集合类型的聚合对象和一个数组类型的聚合对象,但是从客户端来看,访问聚合的代码是完全一样的,根本看不出任何的差别,也看不出到底聚合对象内部是什么类型。