设计模式学习(十):lterator迭代器模式

news2024/11/16 17:47:09

一、什么是Iterator模式

使用Java语言显示数组arr中的元素时,我们可以使用下面这样的for循环语句遍历数组。

for (int i = 0; i < arr.length; i++){
    system.out.println(arr[i]);
}

请注意这段代码中的循环变量i。该变量的初始值是o,然后会递增为1,2,3,...,程序则在每次i递增后都输出arr[i]。我们在程序中经常会看到这样的for循环语句。

for语句中的i++的作用是让i的值在每次循环后自增1,这样就可以访问数组中的下一个元素、下下一个元素、再下下一个元素,也就实现了从头至尾逐一遍历数组元素的功能。

将这里的循环变量i的作用抽象化、通用化后形成的模式,在设计模式中称为Iterator模式。lterator模式用于在数据集合中按照顺序遍历集合。英语单词Iterate有反复做某件事情的意思,汉语称为“迭代器”。

用一句话来概括:一个一个遍历

二、Iterator模式示例代码

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

2.1 类之间的关系

类和接口的功能:

类图:

2.2 Aggregate接口

Aggregate接口是所要遍历的集合的接口。实现了该接口的类将成为一个可以保存多个元素的集合,就像数组一样。Aggregate有“使聚集”、“集合”的意思。

public interface Aggregate {
    public abstract Iterator iterator();
}

2.3 lterator接口

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

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

/**
 * 用于遍历集合中的元素
 */
public interface Iterator {
    /**
     * 判断是否存在下一个元素
     * @return 当集合中存在下一个元素时,该方法返回true
     *         当集合中不存在下一个元素,即已经遍历至集合末尾时,返回false
     */
    public abstract boolean hasNext();

    /**
     * 获取下一个元素
     * @return 下一个元素
     */
    public abstract Object next();
}

2.4 Book类

Book类是表示书的类。

public class Book {
    //书名
    private String name;

    public Book(String name) {
        this.name = name;
    }

    /**
     * 获取书的名字
     */
    public String getName() {
        return name;
    }
}

2.5 BookShelf类

BookShelf类是表示书架的类。由于需要将该类作为集合进行处理,因此它实现了Aggregate接口。此外,请注意在BookShelf类中还实现了Aggregate接口的iterator方法。

之所以将books字段的可见性设置为private,是为了防止外部不小心改变了该字段的值。

接下来我们看看iterator方法。该方法会生成并返回BookShelfIterator类的实例作为BookShelf类对应的Iterator。当外部想要遍历书架时,就会调用这个方法。

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;
    }

    @Override
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

2.6 BookShelflterator类

用于遍历书架的类。

public class BookShelfIterator implements Iterator {
    //要遍历的书架
    private BookShelf bookShelf;
    //迭代器当前所指向的书的下标
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }

    /**
     * 判断书架中还有没有下一本书
     * @return 如果有就返回true,如果没有就返回false
     */
    @Override
    public boolean hasNext() {
        //比较index和书架中书的总册数
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 返回迭代器当前所指向的书(Book的实例),并让迭代器指向下一本书
     */
    @Override
    public Object next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

2.7 Main类

public class Main {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);
        bookShelf.appendBook(new Book("Romance of the Three Kingdoms"));
        bookShelf.appendBook(new Book("A Dream in Red Mansions"));
        bookShelf.appendBook(new Book("The Story of Stone"));
        bookShelf.appendBook(new Book("Journey to the West"));

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

2.8 运行结果

通过bookShelf.iterator()得到的it是用于遍历书架的Iterator实例。while部分的条件当然就是it.hasNext()了。只要书架上有书,while循环就不会停止。然后,程序会通过it.next()一本—本地遍历书架中的书。

三、拓展思路的要点

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

为什么一定要考虑引入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类型了。这表明,这段程序就是要使用Iterator的方法进行编程,而不是BookShelfIterator的方法。

3.2 难以理解抽象类和接口

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

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

3.3 Aggregate和 Iterator的对应

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

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

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

3.4 容易弄错“下一个”

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

returnCurrentElementAndAdvanceToNextPosition

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

3.5 还容易弄错“最后一个”

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

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

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

3.6 迭代器的种类多种多样

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

  • 从最后开始向前遍历

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

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

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

3.7 不需要deletelterator

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

四、相关的设计模式

4.1 Visitor模式

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

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

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

4.2 Composite模式

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

4.3 Factory Method模式

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

设计模式学习(七):Factory Method工厂模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/173507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

halo 1.4.17 使用Mysql 安装与配置

1 下载代码 https://github.com/halo-dev/halo/archive/refs/tags/v1.4.17.zip 2 查看1.4版本文档 https://docs.halo.run/1.4/ 1.3 使用idea打开并设置jdk 11 1.4 将h2配置成为mysql 修改前 修改后 1.5 打包成jar halo使用的是Gradle&#xff0c;打包时&#xff0c…

微软官宣裁员 10000 人。分享一些我的建议给大家

大家好&#xff01;我是韩老师。昨天&#xff0c;西雅图双雄经历着不眠之夜。早些时间&#xff0c;就有传言说 1 月 18 日&#xff0c;亚马逊会裁员 18000 人。微软要裁员的各种消息也是满天飞。北京时间昨天晚上&#xff0c;微软官方博客发了一篇标题为 Focusing on our short…

10. 元组tuple类型详解

python3 tuple类型的使用 1. 基本知识 a. 元组&#xff08;tuple&#xff09;与列表类似, 不同之处在于元组的元素(项)不能修改。 b. 元组写在小括号 () 里&#xff0c;元素之间用逗号隔开。 c. 元组中的元素类型也可以不相同。 d. 构造包含0个或1个元素的元组比较特殊, 所以…

自增主键为什么不是连续的?

在前面文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑。 之前我见过有的业务设计依赖于自增主键的连续性,也就是说,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不能保证连续递…

【深度学习数学基础之线性代数】研究使用链式法则进行反向传播的求导算法

链式法则 简单的说链式法则就是原本y对x求偏导&#xff0c;但是由于过程较为复杂&#xff0c;我们需要将函数进行拆分&#xff0c;通过链式进行分别求导&#xff0c;这样会使整个计算更为简单。 假设f k ( a b c ) f k(a bc)fk(abc) 通俗来说&#xff0c;链式法则表明&a…

宝贝代码部署笔记

记录前后端分离项目部署到云服务器 文章目录1. 启动数据库2. 创建数据库3. 阿里云开放后端项目端口4. 运行SQL文件5. 打包前端文件6. 服务端创建文件夹7. 打包后端jar包8. 安装配置Nginx服务器9. 启动Tomcat10. 项目文件上传部署1. 启动数据库 使用命令cd /opt/mysql/support-…

Generative Adversarial Network (GANs) 对抗神经网络 基础 第一部分

Generative Adversarial Network (GANs) 对抗神经网络 基础 第一部分 定义 Definition Discriminative model&#xff1a; Classifier 判别器Generative model: (random set of value , class) as input -> Create new features X 生成器 对抗神经网络模型主要就是通过判…

android的system域解耦

google很早在为此做准备&#xff0c;要求所有设备能够刷GSI&#xff08;通用系统镜像&#xff09;&#xff0c;并跑过XTS测试。动态分区解耦方案如上图。一、分区描述单一系统映像 (SSI)。包含system和system_ext图像的新概念图像。当这些分区对于一组目标设备是通用的时&#…

二叉树(一)

先简单了解一下树的概念&#xff0c;从而进一步了解二叉树&#xff0c;最后进行代码测试。树概念及结构(了解)在认识而二叉树之前我们首先了解一下树的概念。树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。…

图扑喜获第十一届中国创新创业大赛全国赛优秀奖!

在近期结束的第十一届中国创新创业大赛全国赛&#xff08;新一代信息技术&#xff09;比赛中&#xff0c;图扑软件喜获成长组优秀奖。这是继“创客中国”创新创业大赛优胜奖荣誉后&#xff0c;再一次对图扑软件在新一代信息技术领域专业的认可&#xff01;大赛围绕新一代信息技…

电机行业EDI案例分析

项目背景 J公司需要与国内某知名电机品牌Z公司建立EDI对接&#xff0c;J公司选择通过知行EDI系统与Z公司建立AS2连接&#xff0c;通过AS2接收Z公司发送过来的ORDERS&#xff08;采购订单&#xff09;和ORDCHG&#xff08;采购订单变更&#xff09;&#xff0c;并根据发接收到的…

Linux常见命令 15 - 权限管理命令 chmod

1. chmod 语法 chmod为修改文件/文件夹权限&#xff0c;有以下两种操作&#xff0c;其中-R表示递归修改 chmod {ugoa} {-} {rwx} [文件或目录] -Rchmod [mode421] [文件或目录] -R 2. chmod {ugoa} {-} {rwx} [文件或目录] -R u&#xff1a;文件或目录的所有者&#xff0c;g…

C++设计模式(5)——观察者模式

观察者模式 亦称&#xff1a; 事件订阅者、监听者、Event-Subscriber、Listener、Observer 意图 观察者模式是一种行为设计模式&#xff0c; 允许你定义一种订阅机制&#xff0c; 可在对象事件发生时通知多个 “观察” 该对象的其他对象。 问题 假如你有两种类型的对象&a…

概论第6章_正态总体的抽样分布_卡方分布_F分布_t分布

一 卡方分布 定义 设X1,X2,...,XnX_1, X_2,..., X_nX1​,X2​,...,Xn​ 独立同分布于标准正态分布N(0, 1), 则χ2X12...Xn2\chi^2X_1^2 ... X_n^2χ2X12​...Xn2​的分布称为 自由度为 n 的χ2\chi^2χ2分布&#xff0c; 记为χ2\chi^2χ2 ~ χ2(n)\chi^2(n)χ2(n) χ2\chi…

Python爬虫序章---爬取csdn作者排行榜

上篇文章介绍了requests库获取数据的基本方法&#xff0c;本篇文章利用自动化测试工具selenium进行数据抓取&#xff0c;也会对代码部分进行详细解释&#xff0c;以便小伙伴们能够更加理解和上手。 一.selenium技术介绍 Selenium是最广泛使用的开源 Web UI&#xff08;用户界面…

windows11远程连接Ubuntu桌面

如何通过Windows 11远程连接Ubuntu桌面 在日常开发过程中&#xff0c;很多时候是这样一种情形&#xff1a;一台装了Ubuntu系统的计算机作为远程服务器&#xff0c;开发人员则使用带Windows系统的计算机去连服务器进行开发。 连接服务器的方式有很多种&#xff0c;最简单的就是…

图扑软件荣获第十一届中国创新创业大赛全国赛优秀奖!

在近期结束的第十一届中国创新创业大赛全国赛&#xff08;新一代信息技术&#xff09;比赛中&#xff0c;图扑软件喜获成长组优秀奖。这是继“创客中国”创新创业大赛优胜奖荣誉后&#xff0c;再一次对图扑软件在新一代信息技术领域专业的认可&#xff01;大赛围绕新一代信息技…

DW动手学数据分析Task4:数据可视化

目录1 了解matplotlib2 可视化图案3 matplotlib用法4 了解Seaborn1 了解matplotlib Matplotlib&#xff1a; 是 Python 的绘图库&#xff0c; 它可与 NumPy 一起使用&#xff0c;提供了一种有效的 MatLab 开源替代方案。 2 可视化图案 基本可视化团及场景使用 柱状图 场景&am…

如何实现机械臂的正解计算?

1. 机械臂运动学介绍 机械臂运动学 机器人运动学就是根据末端执行器与所选参考坐标系之间的几何关系&#xff0c;确定末端执行器的空间位置和姿态与各关节变量之间的数学关系。包括正运动学&#xff08;Forward Kinematics&#xff09;和逆运动学&#xff08;Inverse Kinematic…

在线支付系列【3】支付安全之对称和非对称加密

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录前言信息安全加密机制核心概念对称加密非对称加密JCE对称加解密1. 创建密钥2. 加密3. 解密非对称加解密1. 创建密钥2. 公钥加密3. 私钥解密前言 支付和金钱挂钩&#xff0c;支付安全显得尤为重…