本文参(chao)考(xi)《图解设计模式》 结城浩 (作者) 杨文轩 (译者)

1.Iterator 模式

迭代器作用于集合,是用来遍历集合元素的对象。

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器封装了对集合的遍历,使得不用了解集合的内部细节,就可以使用同样的方式遍历不同的集合。

迭代器模式属于行为型模式。

2.示例

首先,让我们来看一段实现了Iterator 模式的示例程序。这段示例程序的作用是将书(Book)放置到书架(BookShelf)中,并将书的名字按顺序显示出来:

Aggregate 接口:

是所要遍历的集合的接口。实现了该接口的类将成为一个可以保存多个元素的集合:

  public interface Aggregate {
public abstract Iterator iterator();//生成一个实现了Iterator接口的类的实例
}

Iterator 接口:

Iterator 接口用于遍历集合中的元素,其作用相当于循环语句中的循环变量。最简单的Iterator 接口如下:

 public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}

这里我们声明了两个方法:

hasNext()方法:返回值是boolean 类型。其原因很容易理解,当集合中存在下一个元素时,该方法返回true ;当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回false。hasNext 方法主要用于循环终止条件。

next()方法:返回类型是Object。这表明该方法返回的是集合中的一个元素。但是,next 方法的作用并非仅仅如此。为了能够在下次调用next 方法时正确地返回下一个元素,该方法中还隐含着将迭代器移动至下一个元素的处理。说“隐含”,是因为Iterator 接口只知道方法名。想要知道next 方法中到底进行了什么样的处理,还需要看一下实现了Iterator 接口的类(BookShelfIterator)。这样,我们才能看懂next 方法的作用。

Book 类:

Book 类是表示书的类。提供getName() 方法获取书名。

 public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

BookShelf 类:

BookShelf 类是表示书架的类。集合Aggregate的实现类。

 public class BookShelf implements Aggregate {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
}

BookShelfIteraotr 类:

Iterator 接口的具体实现方法。

 public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;//遍历对象
private int index;//下标
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}

构造函数BookShelfIterator():将接收到的BookShelf的实例保存在bookShelf 字段中,并将index 初始化为0。

hasNext()方法:该方法将会判断书架中还有没有下一本书,如果有就返回true,如果没有就返回false。而要知道书架中有没有下一本书,可以通过比较index 和书架中书的总册数(bookShelf.getLength() 的返回值)来判断。

next()方法:会返回迭代器当前所指向的书(Book 的实例),并让迭代器指向下一本书。next()方法稍微有些复杂,它首先取出book 变量作为返回值,然后让index 指向后面一本书。

Main 类:

至此,遍历书架的准备工作就完成了。一下是测试类:

 public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println(book.getName());
}
}
}

测试输出结果如下:

3.Iterator 模式中的登场角色

通过以上示例,让我们来看看Iterator 模式中的登场角色。

◆ Iterator(迭代器)

该角色负责定义按顺序逐个遍历元素的接口(API)。在示例程序中,由Iterator 接口扮演这个角色,它定义了hasNext 和next 两个方法。其中,hasNext 方法用于判断是否存在下一个元素,next 方法则用于获取该元素。

◆ ConcreteIterator(具体的迭代器)

该角色负责实现Iterator 角色所定义的接口(API)。在示例程序中,由BookShelfIterator 类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf 类的实例保存在bookShelf 字段中,被指向的书的下标保存在index 字段中。

◆ Aggregate(集合)

该角色负责定义创建Iterator 角色的接口(API)。这个接口(API)是一个方法,会创建出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate 接口扮演这个角色,它里面定义了iterator 方法。

◆ ConcreteAggregate(具体的集合)

该角色负责实现Aggregate 角色所定义的接口(API)。它会创建出具体的Iterator 角色,即ConcreteIterator 角色。在示例程序中,由BookShelf 类扮演这个角色,它实现了iterator 方法。Iterator 模式的类图如下:

4.拓展思路的要点

不管实现如何变化,都可以使用Iterator

为什么一定要考虑引入Iterator 这种复杂的设计模式呢?如果是数组,直接使用for 循环语句进行遍历处理不就可以了吗?为什么要在集合之外引入Iterator 这个角色呢?

一个重要的理由是,引入Iterator 后可以将遍历与实现分离开来。请看下面的代码。

 while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println(book.getName());

这里只使用了Iterator 的hasNext 方法和next 方法,并没有调用BookShelf 的方法。也就是说,这里的while 循环并不依赖于BookShelf 的实现。

如果编写BookShelf 的开发人员决定放弃用数组来管理书本,而是用java.util.Vector取而代之,会怎样呢?不管BookShelf 如何变化,只要BookShelf 的iterator 方法能正确地返回Iterator 的实例(也就是说,返回的Iterator 类的实例没有问题,hasNext 和next 方法都可以正常工作),即使不对上面的while 循环做任何修改,代码都可以正常工作。

这对于BookShelf 的调用者来说真是太方便了。设计模式的作用就是帮助我们编写可复用的类。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需要很小的修改即可应对。

这样也就能理解为什么在示例程序中iterator 方法的返回值不是BookShelfIterator 类型而是Iterator 类型了(代码清单1-6)。这表明,这段程序就是要使用Iterator 的方法进行编程,而不是BookShelfIterator 的方法。

难以理解抽象类和接口

难以理解抽象类和接口的人常常使用ConcreteAggregate 角色和ConcreteIterator 角色编程,而不使用Aggregate 接口和Iterator 接口,他们总想用具体的类来解决所有的问题。

但是如果只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易作为组件被再次利用,我们需要引入抽象类和接口。

这也是贯穿本书的思想。即使大家现在无法完全理解,相信随着深入阅读本书,也一定能够逐渐理解。请大家将“不要只使用具体类来编程,要优先使用抽象类和接口来编程”印在脑海中。

Aggregate 和Iterator 的对应

请大家仔细回忆一下我们是如何把BookShelfIterator 类定义为BookShelf 类的ConcreteIterator 角色的。BookShelfIterator 类知道BookShelf 是如何实现的。也正是因为如此,我们才能调用用来获取下一本书的getBookAt 方法。

也就是说,如果BookShelf 的实现发生了改变,即getBookAt 方法这个接口(API)发生变化时,我们必须修改BookShelfIterator 类。

正如Aggregate 和Iterator 这两个接口是对应的一样,ConcreteAggregate 和ConcreteIterator 这两个类也是对应的。

容易弄错“下一个”

在Iterator 模式的实现中,很容易在next 方法上出错。该方法的返回值到底是应该指向当前元素还是当前元素的下一个元素呢?更详细地讲,next 方法的名字应该是下面这样的。

returnCurrentElementAndAdvanceToNextPosition

也就是说,next 方法是“返回当前的元素,并指向下一个元素”。

还容易弄错“ 最后一个”

在Iterator 模式中,不仅容易弄错“下一个”,还容易弄错“最后一个”。hasNext 方法在返回最后一个元素前会返回true,当返回了最后一个元素后则返回false。稍不注意,就会无法正确地返回“最后一个”元素。

请大家将hasNext 方法理解成“确认接下来是否可以调用next 方法”的方法就可以了。

多个Iterator

“将遍历功能置于Aggregate 角色之外”是Iterator 模式的一个特征。根据这个特征,可以针对一个ConcreteAggregate 角色编写多个ConcreteIterator 角色。

迭代器的种类多种多样

在示例程序中展示的Iterator 类只是很简单地从前向后遍历集合。其实,遍历的方法是多种多样的。

● 从最后开始向前遍历

● 既可以从前向后遍历,也可以从后向前遍历(既有next方法也有previous方法)

● 指定下标进行“跳跃式”遍历

学到这里,相信大家应该可以根据需求编写出各种各样的Iterator 类了。

不需要deleteIterator

在Java 中,没有被使用的对象实例将会自动被删除(垃圾回收,GC)。因此,在iterator 中不需要与其对应的deleteIterator 方法。

5.相关的设计模式

◆ Visitor 模式

Iterator 模式是从集合中一个一个取出元素进行遍历,但是并没有在Iterator 接口中声明对取出的元素进行何种处理。

Visitor 模式则是在遍历元素集合的过程中,对元素进行相同的处理。

在遍历集合的过程中对元素进行固定的处理是常有的需求。Visitor 模式正是为了应对这种需求而出现的。在访问元素集合的过程中对元素进行相同的处理,这种模式就是Visitor 模式。

◆ Composite 模式

Composite 模式是具有递归结构的模式,在其中使用Iterator 模式比较困难。

◆ Factory Method 模式

在iterator 方法中生成Iterator 的实例时可能会使用Factory Method 模式。

设计模式-Iterator的更多相关文章

  1. [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式

    Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...

  2. [C# 设计模式] Iterator - 迭代器模式:我与一份奥利奥早餐的故事

    Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...

  3. 使用C# (.NET Core) 实现迭代器设计模式 (Iterator Pattern)

    本文的概念来自深入浅出设计模式一书 项目需求 有两个饭店合并了, 它们各自有自己的菜单. 饭店合并之后要保留这两份菜单. 这两个菜单是这样的: 菜单项MenuItem的代码是这样的: 最初我们是这样设 ...

  4. 1.设计模式-------Iterator

    本文主要是参考<图解设计模式>写的读书笔记: 开发中我用到遍历集合时候,无非我常用的就是简单的for循环,foreach,iterator 这三种方式进行遍历! 当然这三种的效率: 学习I ...

  5. java设计模式-Iterator

    Iterator模式 主要是用在容器的遍历上,其他的地方都不怎么用:理解一下,会用了就可以了:   1.背景 请动手自己写一个可以动态添加对象的容器: 代码: ArrayList.java(是自己实现 ...

  6. [C++设计模式] iterator 迭代器模式

    迭代器模式定义:提供一种方法顺序訪问一个聚合对象中各个元素,而又不须要暴露该对象. 迭代器分内部迭代器和外部迭代器.内部迭代器与对象耦合紧密,不推荐使用. 外部迭代器与聚合容器的内部对象松耦合,推荐使 ...

  7. C++设计模式-Iterator迭代器模式

    ref: http://www.cnblogs.com/onlycxue/archive/2013/12/25/3490738.html

  8. Java设计模式之Iterator模式

    分类: [java]2013-07-15 10:58 917人阅读 评论(0) 收藏 举报 所谓Iterator模式,即是Iterator为不同的容器提供一个统一的访问方式.本文以java中的容器为例 ...

  9. Java 设计模式实现 不错的引用

    这段时间有兴趣重新温习一下设计模式在Java中的实现,碰巧看到一个不错的设计模式总结,这里引用一下作为参考. 创建型模式: JAVA设计模式-Singleton JAVA设计模式-Factory JA ...

随机推荐

  1. 如何在vue项目中引入element-ui

    安装 elementUI npm install element-ui --save 引入elementUI import ElementUI from 'element-ui' import 'el ...

  2. Java新功能之方法引用

    方法引用的使用 最初,引用只是针对引用类型完成的,也就是只有数组.类.接口才具备引用操作.JDK1.8后追加了方法引用.实际上引用的本质就是别名. 因此方法的引用就是别名的使用. 方法的引用有四种形式 ...

  3. docker-compose设置mysql初始化数据库的字符集

    version: '3' services: mysql: image: mysql:5.7.24# volumes:# - ./mysqld.cnf:/etc/mysql/mysql.conf.d/ ...

  4. Oracle IMP-00010 不是有效的导出文件,标头验证失败 解决方法

    用IMP导入dmp文件时,出现IMP-00010 不是有效的导出文件,标头验证失败问题. 第一种:网上搜索到的大多解决方法是说导出文件时使用的Oracle版本不一致问题,需要修改dmp文件的版本号.如 ...

  5. Flink 原理(六)——异步I/O(asynchronous I/O)

    1.前言 本文是基于Flink官网上Asynchronous  I/O的介绍结合自己的理解写成的,若有不正确的欢迎大伙留言交流,谢谢! 2.Asynchronous  I/O简介 将Flink用于流计 ...

  6. react-navigation 做导航栏,发现 Android 上的标题不居中

    在做 React Native 应用的时候,我们常常使用 react-navigation 做导航栏,发现 Android 上的标题不居中,IOS 上没问题. 1 如果只有标题,那就在 headerT ...

  7. 使用 create-react-app 快速构建 React 开发环境

    在终端执行以下命令创建项目: 1.指定创建的项目位置(这里以桌面为例) cd Desktop 2.创建 React 项目 npx create-react-app my-app 3.进入项目并启动 c ...

  8. 配置数据源和配置jpa的yml文件

    spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: j ...

  9. [Apio2010]patrol 巡逻

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 2541  Solved: 1288[Submit][S ...

  10. django 时间格式(全局修改,不用过滤器)

    百度了一圈,很没创意的用过滤器,前端每次显示时间表格都要用过滤器,这种挺烦的.隐约记得以前见过没有用过滤器的.换google https://stackoverflow.com/questions/5 ...