本文继续介绍23种设计模式系列之迭代子模式。
定义
在软件构建过程中,集合对象内部结构常常变化各异,但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为同一种算法在多种集合对象上进行操作提供了可能。
使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。
迭代子(Iterator)模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴漏聚集的内部表象。
为什么聚集需要迭代子
多个对象聚在一起形成的总体称之为聚集(Aggregate),聚集对象是能够包容一组对象的容器对象。聚集依赖于聚集结构的抽象化,具有复杂性和多样性,数组就是最基本的聚集,也是其他Java聚集对象的设计基础。
Java聚集(Collection)对象是实现了共同的java.util.Collection接口的对象,是Java语言对聚集的概念的直接支持。
聚集对象必须提供适当的方法,允许客户端按照一个线性顺序遍历所有元素对象 ,把元素对象提取出来或者删除掉等。一个使用聚集的系统必然会使用这些方法操作聚集对象,因而在使用聚聚的系统演化过程中,会出现两类情况。
◆ 迭代逻辑没有改变,但是需要将一种聚集对象换成另一种聚集,因为不同的聚集具有不同的遍历接口,所以需要修改客户端代码,以便将已有的迭代调用换成新聚集对象所要求的接口。
◆ 聚集不改变,但是迭代方式需要改变,比如原来只需要读取元素和删除元素,但现在需要增加新的;或者原来的迭代仅仅遍历所有的元素,而现在则需要对元素加以过滤等。这时就只好修改聚集对象,修改已有的遍历方法,或者增加新的方法。
显然,出现这种情况是因为所涉及的聚集设计不符合“开-闭”原则,也就是因为没有将不变的结构从系统中抽象出来,与可变成分分割,并将可变部分的各种实现封装起来。一个聪明的做法无疑是应当使用更加抽象的处理方法,使得在进行迭代时,客户端根本无需知道所使用的聚集是哪个类型;而当客户端需要使用全新的迭代逻辑时,只需要引进一个新的迭代子对象即可,根本无需修改聚集对象本身。

迭代子模式模式便是这样的一个抽象化的概念,这一模式之所以能够做到这一点,是因为它将迭代逻辑封装到一个独立的迭代子对象汇总,从而与聚集本身分隔开。迭代子对象是对遍历的抽象化,不同的聚集对象可以提供相同的迭代子对象,从而使客户端无需知道聚集的低层结构,一个聚集可以提供多个不同的迭代子对象,从而使得遍历逻辑的变化不会影响到聚集对象本身。
迭代子模式有两种实现方式,分别是白箱聚集与外禀迭代子和黑箱聚集于内禀迭代子。
白箱聚集与外禀迭代子
如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口。
如果聚集对象为所有对象提供同一个接口,也就是宽接口的话,当然会满足迭代子模式对迭代子对象的要求。但是,这样会破坏对聚集对象的封装。这种提供宽接口的聚集叫做白箱聚集。
由于聚集自己实现迭代逻辑,并向外部提供适当的接口,使得迭代子可以从外部控制聚集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫做游标迭代子(Cursor Iterator)。由于迭代子是在聚集结构之外的,因此这样的迭代子又叫做外禀迭代子(Extrinsic Iterator)。
实现
一个白箱聚集向外界提供访问自己内部元素的接口(称作遍历方法或者Traversing Method),从而使外禀迭代子可以通过聚集的遍历方法实现迭代功能。
因为迭代的逻辑是由聚集对象本身提供的,所以这样的外禀迭代子角色往往仅仅保持迭代的游标位置。
角色
抽象迭代子(Iterator)角色:此抽象角色定义出遍历元素所需的接口。
具体迭代子(ConcreteIterator)角色:此角色实现了Iterator接口,并保持迭代过程中的游标位置。
聚集(Aggregate)角色:此抽象角色给出创建迭代子(Iterator)对象的接口。
具体聚集(ConcreteAggregate)角色:实现了创建迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。
客户端(Client)角色:持有对聚集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。
抽象聚集角色类
这个角色规定出所有的具体聚集必须实现的接口。迭代子模式要求聚集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。
[java] view plaincopy print?
  1. public abstract class Aggregate {
  2. /**
  3. * 工厂方法,创建相应迭代子对象的接口
  4. */
  5. public abstract Iterator createIterator();
  6. }
具体聚集角色类

实现了抽象聚集角色类所要求的接口,也就是createIterator()方法。此外,还有方法getElement()向外界提供聚集元素,而方法size()向外界提供聚集的大小等。
[java] view plaincopy print?
  1. public class ConcreteAggregate extends Aggregate {
  2. private Object[] objArray = null;
  3. /**
  4. * 构造方法,传入聚合对象的具体内容
  5. */
  6. public ConcreteAggregate(Object[] objArray){
  7. this.objArray = objArray;
  8. }
  9. @Override
  10. public Iterator createIterator() {
  11. return new ConcreteIterator(this);
  12. }
  13. /**
  14. * 取值方法:向外界提供聚集元素
  15. */
  16. public Object getElement(int index){
  17. if(index < objArray.length){
  18. return objArray[index];
  19. }else{
  20. return null;
  21. }
  22. }
  23. /**
  24. * 取值方法:向外界提供聚集的大小
  25. */
  26. public int size(){
  27. return objArray.length;
  28. }
  29. }
抽象迭代子角色类
[java] view plaincopy print?
  1. public interface Iterator {
  2. /**
  3. * 迭代方法:移动到第一个元素
  4. */
  5. public void first();
  6. /**
  7. * 迭代方法:移动到下一个元素
  8. */
  9. public void next();
  10. /**
  11. * 迭代方法:是否为最后一个元素
  12. */
  13. public boolean isDone();
  14. /**
  15. * 迭代方法:返还当前元素
  16. */
  17. public Object currentItem();
  18. }
具体迭代子角色类
[java] view plaincopy print?
  1. public class ConcreteIterator implements Iterator {
  2. //持有被迭代的具体的聚合对象
  3. private ConcreteAggregate agg;
  4. //内部索引,记录当前迭代到的索引位置
  5. private int index = 0;
  6. //记录当前聚集对象的大小
  7. private int size = 0;
  8. public ConcreteIterator(ConcreteAggregate agg){
  9. this.agg = agg;
  10. this.size = agg.size();
  11. index = 0;
  12. }
  13. /**
  14. * 迭代方法:返还当前元素
  15. */
  16. @Override
  17. public Object currentItem() {
  18. return agg.getElement(index);
  19. }
  20. /**
  21. * 迭代方法:移动到第一个元素
  22. */
  23. @Override
  24. public void first() {
  25. index = 0;
  26. }
  27. /**
  28. * 迭代方法:是否为最后一个元素
  29. */
  30. @Override
  31. public boolean isDone() {
  32. return (index >= size);
  33. }
  34. /**
  35. * 迭代方法:移动到下一个元素
  36. */
  37. @Override
  38. public void next() {
  39. if(index < size)
  40. {
  41. index ++;
  42. }
  43. }
  44. }
客户端类
[java] view plaincopy print?
  1. public class Client {
  2. public void operation(){
  3. Object[] objArray = {"One","Two","Three","Four","Five","Six"};
  4. //创建聚合对象
  5. Aggregate agg = new ConcreteAggregate(objArray);
  6. //循环输出聚合对象中的值
  7. Iterator it = agg.createIterator();
  8. while(!it.isDone()){
  9. System.out.println(it.currentItem());
  10. it.next();
  11. }
  12. }
  13. public static void main(String[] args) {
  14. Client client = new Client();
  15. client.operation();
  16. }
  17. }
上面的例子首先创建了一个聚集类实例,然后调用聚集对象的工厂方法createIterator()以得到一个迭代子对象。在得到迭代子的实例后,客户端开始迭代过程,打印出所有的聚集元素。
意义
一个常常会问的问题是:既然白箱聚集已经向外界提供了遍历方法,客户端已经可以自行进行迭代了,为什么还要应用迭代子模式,并创建一个迭代子对象进行迭代呢?
客户端当然可以自行进行迭代,不一定非得需要一个迭代子对象。但是,迭代子对象和迭代模式会将迭代过程抽象化,将作为迭代消费者的客户端与迭代负责人的迭代子责任分隔开,使得两者可以独立的演化。在聚集对象的种类发生变化,或者迭代的方法发生改变时,迭代子作为一个中介层可以吸收变化的因素,而避免修改客户端或者聚集本身。
此外,如果系统需要同时针对几个不同的聚集对象进行迭代,而这些聚集对象所提供的遍历方法有所不同时,使用迭代子模式和一个外界的迭代子对象是有意义的。具有同一迭代接口的不同迭代子对象处理具有不同遍历接口的聚集对象,使得系统可以使用一个统一的迭代接口进行所有的迭代。
黑箱聚集与内禀迭代子
如果一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是所谓的窄接口
聚集对象为迭代子对象提供一个宽接口,而为其他对象提供一个窄接口。换言之,聚集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象能够对聚集对象有足够的了解,从而可以进行迭代操作。但是,聚集对象应当避免向其他的对象提供这些方法,因为其他对象应当经过迭代子对象进行这些工作,而不是直接操控聚集对象。
在JAVA语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类。这样迭代子对象将可以像聚集对象的内部成员一样访问聚集对象的内部结构。下面给出一个示意性的实现,说明这种双重接口的结构时怎么样产生的,以及使用了双重接口结构之后迭代子模式的实现方案。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做黑箱实现方案
由于迭代子是聚集的内部类,迭代子可以自由访问聚集的元素,所以迭代子可以自行实现迭代功能并控制对聚集元素的迭代逻辑。由于迭代子是在聚集的结构之内定义的,因此这样的迭代子又叫做内禀迭代子(Intrinsic Iterator)。
 
应用
为了说明黑箱方案的细节,这里给出一个示意性的黑箱实现。在这个实现里,聚集类ConcreteAggregate含有一个内部成员类ConcreteIterator,也就是实现了抽象迭代子接口的具体迭代子类,同时聚集并不向外界提供访问自己内部元素的方法。
抽象聚集角色类
这个角色规定出所有的具体聚集必须实现的接口。迭代子模式要求聚集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。
[java] view plaincopy print?
  1. public abstract class Aggregate {
  2. /**
  3. * 工厂方法,创建相应迭代子对象的接口
  4. */
  5. public abstract Iterator createIterator();
  6. }

抽象迭代子角色类
[java] view plaincopy print?
  1. public interface Iterator {
  2. /**
  3. * 迭代方法:移动到第一个元素
  4. */
  5. public void first();
  6. /**
  7. * 迭代方法:移动到下一个元素
  8. */
  9. public void next();
  10. /**
  11. * 迭代方法:是否为最后一个元素
  12. */
  13. public boolean isDone();
  14. /**
  15. * 迭代方法:返还当前元素
  16. */
  17. public Object currentItem();
  18. }
具体聚集角色类
实现了抽象聚集角色所要求的接口,也就是createIterator()方法。此外,聚集类有一个内部成员类ConcreteIterator,这个内部类实现了抽象迭代子角色所规定的接口;而工厂方法createIterator()所返还的就是这个内部成员类的实例。
[java] view plaincopy print?
  1. public class ConcreteAggregate extends Aggregate {
  2. private Object[] objArray = null;
  3. /**
  4. * 构造方法,传入聚合对象的具体内容
  5. */
  6. public ConcreteAggregate(Object[] objArray){
  7. this.objArray = objArray;
  8. }
  9. @Override
  10. public Iterator createIterator() {
  11. return new ConcreteIterator();
  12. }
  13. /**
  14. * 内部成员类,具体迭代子类
  15. */
  16. private class ConcreteIterator implements Iterator
  17. {
  18. //内部索引,记录当前迭代到的索引位置
  19. private int index = 0;
  20. //记录当前聚集对象的大小
  21. private int size = 0;
  22. /**
  23. * 构造函数
  24. */
  25. public ConcreteIterator(){
  26. this.size = objArray.length;
  27. index = 0;
  28. }
  29. /**
  30. * 迭代方法:返还当前元素
  31. */
  32. @Override
  33. public Object currentItem() {
  34. return objArray[index];
  35. }
  36. /**
  37. * 迭代方法:移动到第一个元素
  38. */
  39. @Override
  40. public void first() {
  41. index = 0;
  42. }
  43. /**
  44. * 迭代方法:是否为最后一个元素
  45. */
  46. @Override
  47. public boolean isDone() {
  48. return (index >= size);
  49. }
  50. /**
  51. * 迭代方法:移动到下一个元素
  52. */
  53. @Override
  54. public void next() {
  55. if(index < size)
  56. {
  57. index ++;
  58. }
  59. }
  60. }
  61. }

客户端类

[java] view plaincopy print?
  1. public class Client {
  2. public void operation(){
  3. Object[] objArray = {"One","Two","Three","Four","Five","Six"};
  4. //创建聚合对象
  5. Aggregate agg = new ConcreteAggregate(objArray);
  6. //循环输出聚合对象中的值
  7. Iterator it = agg.createIterator();
  8. while(!it.isDone()){
  9. System.out.println(it.currentItem());
  10. it.next();
  11. }
  12. }
  13. public static void main(String[] args) {
  14. Client client = new Client();
  15. client.operation();
  16. }
  17. }

上面的例子首先创建了一个聚集类实例,然后调用聚集对象的工厂方法createIterator()以得到一个迭代子对象。在得到迭代子的实例后,客户端开始迭代过程,打印出所有的聚集元素。

迭代子模式优点
  1. 迭代子模式简化了聚集的接口。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。
  2. 每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。
  3. 由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。
更多设计模式:23种设计模式系列

作者:jason0539

博客:http://blog.csdn.net/jason0539(转载请说明出处

转载于:https://www.cnblogs.com/telwanggs/p/6781800.html

Java设计模式之迭代子模式相关推荐

  1. 【java设计模式】迭代子模式

    <JAVA与模式>之迭代子模式 在阎宏博士的<JAVA与模式>一书中开头是这样描述迭代子(Iterator)模式的: 迭代子模式又叫游标(Cursor)模式,是对象的行为模式. ...

  2. Java 设计模式 Iterator 迭代 模式

    Java 设计模式 Iterator 迭代 模式 Iterator模式用于在数据集合中按照顺序遍历集合. 涉及到的角色 迭代器:负责定义按顺序逐个遍历元素的接口. 具体的迭代器:负责实现迭代器角色定义 ...

  3. Java二十三设计模式之------迭代子模式

    一.迭代子模式(Iterator) 顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松.这句话包含两层意思:一是需要遍历的对象,即聚 ...

  4. 设计模式之迭代子模式

    迭代子模式又叫游标(Cursor)模式,是对象的行为模式. 迭代子模式的定义 迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象.我们常见的集合有很多种类,其顶层数据存储和组织方式的不同 ...

  5. Java设计模式之五大创建型模式(附实例和详解)

    一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥 ...

  6. Java设计模式理论知识要点总结

    一 设计模式基本知识 1. 定义 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证 ...

  7. Java设计模式(一) -- 工厂方法模式

    java的设计模式大体上分为三大类: •创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式. •结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组 ...

  8. Java设计模式详解

    设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  9. Java设计模式、框架、架构、平台之间的关系

     1.设计模式 为什么要先说设计模式?因为设计模式在这些概念中是最基本的,而且也比较简单.那么什么是设计模式呢?说的直白点,设计模式就是告诉你针对特定问题如何组织类.对象和接口之间的关系,是前人总结的 ...

最新文章

  1. php源码安全加密之PHP混淆算法.
  2. SpringBoot中的自定义路径怎么配置/根目录配置方法
  3. 【双100%解法】剑指 Offer 24. 反转链表
  4. 基于easyui开发Web版Activiti流程定制器详解(二)——文件列表
  5. Eureka Server启动源码分析
  6. chrome vue插件_不容错过的 Chrome 插件推荐合集-开发者必备篇
  7. C#中IL反汇编工具的使用 其具体含义如下文
  8. 软件测试【个人简历】展示模板
  9. gtp6 linux 启动_glibc.i686安装
  10. 贴片电容COG、NPO、X7R、Y5V、X5R介质的区别
  11. 台式计算机怎么设置自动锁屏,台式机win7怎么设置自动锁屏
  12. 开关电源初级和次级变压器之间的Y电容作用
  13. 拼多多店铺什么时候推广好?
  14. 史上最全recyclewView集合,下拉刷新,上拉加载,左滑删除,点击按钮滑动到指定位置
  15. 网点分布图怎么做,用地图制作客户分布图
  16. 【软件通信协议】1. 详细解析TCP/IP通信协议
  17. 怎么将计算机恢复到前一天的状况,excel表格恢复前一天数据-我想将excel表格中的两组数据做对比(数据是每天变......
  18. Qt美化之基础控件美化
  19. 「DR老兵寻访」游戏正规军的“万智牌生态”链改计划
  20. 超级计算机的内部图,中科院首次获得了宇宙中全尺度暗晕内部结构的清晰图像...

热门文章

  1. 如何解决亚稳态?(FPGA面试题)
  2. mysql binlog生成异常_mysql binlog故障演练
  3. 如何通过cmd网站服务器地址,如何用cmd进入服务器地址
  4. mapreduce分组统计_mongodb中使用mapreduce进行分组统计
  5. imp 只导入索引_Elasticsearch系列---实战零停机重建索引
  6. 【C语言】常用字符(string库函数,ctype库函数),字符数组的输入与处理)
  7. Redis面试之传统五大数据类型的落地应用详解
  8. javascript基础知识(3) 基本语法
  9. [Luogu 1312] noip11 Mayan游戏
  10. WIN7系统程序放在中文文件夹打开报错及界面汉字变乱码