行为型设计模式06-迭代器模式

news2024/11/18 3:31:27

🧑‍💻作者:猫十二懿

❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github

🎉公众号:猫十二懿

迭代器模式

1、迭代器模式介绍

迭代器模式是一种行为型设计模式,它提供了一种方法来访问聚合对象中的各个元素,而不暴露其内部表示。通过使用迭代器,客户端可以遍历一个聚合对象中的元素,而不必了解其内部实现。

在迭代器模式中,定义了一个迭代器接口,该接口声明了能够访问聚合对象中元素的方法。然后,具体的迭代器实现类实现了这个接口,并提供了一个具体的遍历算法。聚合对象则负责创建并返回其对应的迭代器实例。

例如:在以前坐公交得自己交钱,售票员根据上车的人进行售票,不管什么人都要买票(比如小偷、公交车公司内部人员、程序员等同样要买票人人平等)。售票员其实就是将所有人都遍历了一遍,对每个乘客都要买票。

1.1 迭代器模式基本实现

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

迭代器模式结构图:

image-20230513205853439

Aggregate聚集抽象类:

/**
 * @author Shier
 * CreateTime 2023/5/13 21:07
 * 聚集抽象类
 */
public abstract class Aggregate {
    /**
     * 创建迭代器
     *
     * @return
     */
    public abstract Iterator createIterator();
}

ConcreteAggregate具体聚集类:继承Aggregate。

/**
 * @author Shier
 * CreateTime 2023/5/13 21:09
 * 具体聚集类
 */
public class ConcreteAggregate extends Aggregate {
    /**
     * 存储聚合对象
     */
    private List<Object> list = new ArrayList<>();


    @Override
    public Iterator createIterator() {
        return (Iterator) new ConcreteIterator(this);
    }

    /**
     * 返回聚合总个数
     *
     * @return
     */
    public int getCount() {
        return list.size();
    }

    /**
     * 增加新对象
     *
     * @param obj
     */
    public void add(Object obj) {
        list.add(obj);
    }

    /**
     * 得到指定的对象
     */
    public Object getCurrentItem(int index) {
        return list.get(index);
    }
}

Iterator迭代器抽象类:

/**
 * @author Shier
 * CreateTime 2023/5/13 21:16
 * Iterator 迭代器对象类
 */
public abstract class Iterator {
    // 第一个
    public abstract Object first();

    // 下一个
    public abstract Object netx();

    // 是否到最后
    public abstract boolean isDone();

    // 当前对象
    public abstract Object currentItems();
}

ConcreteIterator具体迭代器类:继承Iterator。

/**
 * @author Shier
 * CreateTime 2023/5/13 21:15
 */
public class ConcreteIterator extends Iterator {
    private ConcreteAggregate aggregate;

    public ConcreteIterator(ConcreteAggregate aggregate) {
        this.aggregate = aggregate;
    }

    private int current = 0;

    /**
     * 得到第一个对象
     *
     * @return
     */
    @Override
    public Object first() {
        return aggregate.getCurrentItem(0);
    }

    /**
     * 得到下一个对象
     *
     * @return
     */
    @Override
    public Object netx() {
        Object ret = null;
        current++;
        if (current < aggregate.getCount()) {
            ret = aggregate.getCurrentItem(current);
        }
        return ret;
    }

    /**
     * 判断是否到结尾
     *
     * @return
     */
    @Override
    public boolean isDone() {
        return current >= aggregate.getCount() ? true : false;
    }

    /**
     * 返回当前对象
     *
     * @return
     */
    @Override
    public Object currentItems() {
        return aggregate.getCurrentItem(current);
    }
}

客户端代码:

/**
 * @author Shier
 * CreateTime 2023/5/13 21:27
 */
public class BaseClient {
    public static void main(String[] args) {

        // 聚集对象 上面的例子就相当于公交车bus
        ConcreteAggregate aggregateBus = new ConcreteAggregate();
        aggregateBus.add("shier");
        aggregateBus.add("公交公司员工");
        aggregateBus.add("小菜");
        aggregateBus.add("大白");
        aggregateBus.add("小黑");
        aggregateBus.add("小偷");

        // 迭代器对象声明,即相当于售票员
        ConcreteIterator conductor = new ConcreteIterator(aggregateBus);
        // 向第一个乘客售票
        conductor.first();
        while (!conductor.isDone()) {
            // 没有到最后一个则一直走向下一个乘客
            System.out.println(conductor.currentItems() + ": 请买票!");
            conductor.netx();
        }
    }
}

输出结果:

image-20230513214557626

同时还可以实现倒序:

/**
 * @author Shier
 * CreateTime 2023/5/13 21:15
 * 具体迭代器类 - 倒序
 */
public class ConcreteIteratorDesc extends Iterator {
    private ConcreteAggregate aggregate;

    private int current = 0;

    public ConcreteIteratorDesc(ConcreteAggregate aggregate) {
        this.aggregate = aggregate;
        // 从最后一个开始
        current = aggregate.getCount() - 1;
    }

    /**
     * 得到倒数第一个对象
     *
     * @return
     */
    @Override
    public Object first() {
        return aggregate.getCurrentItem(aggregate.getCount() - 1);
    }

    /**
     * 得到下一个对象
     *
     * @return
     */
    @Override
    public Object netx() {
        Object ret = null;
        // 递减
        current--;
        if (current < aggregate.getCount()) {
            ret = aggregate.getCurrentItem(current);
        }
        return ret;
    }

    /**
     * 判断是否到结尾
     *
     * @return
     */
    @Override
    public boolean isDone() {
        return current >= aggregate.getCount() ? true : false;
    }

    /**
     * 返回当前对象
     *
     * @return
     */
    @Override
    public Object currentItems() {
        return aggregate.getCurrentItem(current);
    }
}

你想呀,售票员才不管你上来的是人还是物(行李),不管是中国人还 是外国人,不管是不是内部员工,甚至哪怕是马上要抓走的小偷,只要是来 乘车的乘客,就必须要买票。同样道理,当你需要访问一个聚集对象,而且 不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。对聚集有多种方式遍历时,可以考虑用迭代器模式。售票员从车头到车尾来售票,也可以从车尾向车头来售票,也就是说, 你需要==对聚集有多种方式遍历时,可以考虑用迭代器模式==。由于不管乘客是 什么,售票员的做法始终是相同的,都是从第一个开始,下一个是谁,是否 结束,当前售到哪个人了,这些方法每天他都在做,也就是说,为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口

2、具体例子说明

2.1 购物车案例

上面也是举例一个坐公交车的列子

下面在用一个例子说明:

  1. 假设有一个超市的购物车,其中包含了多种商品,比如苹果、橙子、香蕉等。这个购物车可以看作是一个聚合对象,而其中每种商品则可以看作是购物车中的元素。

如果要遍历购物车中的所有商品,可以使用迭代器模式来实现。首先,定义一个迭代器接口,声明能够访问购物车中商品的方法。比如,可以定义一个Iterator接口,其中包含hasNext()next()两个方法。

  1. 然后,具体的迭代器实现类可以实现这个接口,并提供一个具体的遍历算法。例如,可以定义一个CartIterator类,它维护了购物车的内部状态,并实现了hasNext()next()方法,通过这两个方法来依次返回购物车中的每个商品。

  2. 最后,购物车可以负责创建并返回其对应的迭代器实例。可以定义一个Cart类,其中包含了购物车中所有商品的列表,以及一个getIterator()方法,用于返回一个CartIterator实例。

这样,客户端就可以通过调用getIterator()方法来获取购物车对应的迭代器,并使用它来遍历购物车中的所有商品。这种实现方式让遍历算法与聚合对象分离开来,使代码更加灵活和易于维护。

首先,定义迭代器接口 CartIterator

public interface CartIterator {
    boolean hasNext();
    Object next();
}

然后,定义具体的迭代器实现类 CartIteratorImpl

public class CartIteratorImpl implements CartIterator {

    private List<Object> cartList;  // 购物车中的商品列表
    private int position;           // 迭代器当前位置

    public CartIteratorImpl(List<Object> cartList) {
        this.cartList = cartList;
        this.position = 0;
    }

    @Override
    public boolean hasNext() {
        return position < cartList.size();
    }

    @Override
    public Object next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        Object item = cartList.get(position);
        position++;
        return item;
    }
}

CartIteratorImpl 类中,我们使用了一个 List<Object> 来保存购物车中的商品列表,在 hasNext()next() 方法中维护了内部状态 position,以便顺序地遍历购物车中的每个商品。

最后,定义聚合对象 Cart,并在其中实现 getIterator() 方法:

public class Cart {
    private List<Object> itemList;

    public Cart() {
        this.itemList = new ArrayList<>();
    }

    public void addItem(Object item) {
        itemList.add(item);
    }

    public void removeItem(Object item) {
        itemList.remove(item);
    }

    public CartIterator getIterator() {
        return new CartIteratorImpl(itemList);
    }
}

Cart 类中,我们使用了一个 List<Object> 来保存购物车中的商品列表,实现了添加和删除商品的方法。同时,实现了 getIterator() 方法,用于返回购物车对应的迭代器实例 CartIteratorImpl

最后,可以在客户端代码中使用 CartCartIterator 来遍历购物车中的所有商品:

public class Client {
    public static void main(String[] args) {
        Cart cart = new Cart();
        cart.addItem("苹果");
        cart.addItem("橙子");
        cart.addItem("香蕉");

        CartIterator iterator = cart.getIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

输出结果:

苹果
橙子
香蕉

2.2 Java迭代器实现

起始在开发过程中不会向上面那样去搞,因为目前很多开发语言以及接入了这个迭代器模式,比如在Java中以及有了一个Iterator类

image-20230513214853068

Java 中的 Iterator 类是迭代器模式的一种典型实现。它提供了一种统一的遍历方式,可以对不同类型的集合进行遍历,而无需关心集合内部的实现细节。

在 Java 中,每个实现了 Iterable 接口的对象都可以返回一个 Iterator 对象,用于遍历它所包含的元素。Iterator 接口定义了三个方法:

  • boolean hasNext():判断集合中是否还有下一个元素;
  • E next():返回集合中的下一个元素;
  • void remove():在迭代过程中移除集合中的当前元素,可选操作。

通过调用 hasNext()next() 方法,可以依次访问集合中的每个元素,直到遍历完所有元素为止。在遍历过程中,Iterator 对象负责维护内部状态,以便正确地返回下一个元素。

在迭代器模式中,迭代器对象将遍历算法和集合对象分离开来,避免了暴露集合内部实现细节,并将集合的遍历行为抽象为一个独立的接口。这使得代码更灵活、可扩展、易于维护。

比如,在 Java 中,可以使用 ArrayListLinkedListHashSet 等不同类型的集合类,并使用 Iterator 接口来实现对它们的遍历,而无需关心它们内部的实现细节。

3、迭代器模式总结

迭代器模式是一种行为型设计模式,它提供了一种访问集合对象内部元素的方式,而不用暴露集合的内部细节。

在迭代器模式中,集合对象和迭代器对象分别负责实现集合和遍历算法,并相互独立地进行演化和修改。这样可以避免暴露集合内部结构,也方便对集合进行扩展和修改。

迭代器模式主要由四个角色组成:

  • 抽象聚合类(Aggregate):定义集合对象的接口,包括添加、删除元素等方法。
  • 具体聚合类(ConcreteAggregate):实现抽象聚合类接口,存储集合中的元素。
  • 抽象迭代器类(Iterator):定义遍历集合的接口,包括获取下一个元素、判断是否还有下一个元素等方法。
  • 具体迭代器类(ConcreteIterator):实现抽象迭代器接口,负责对集合进行遍历操作。

迭代器模式的优点包括:

  • 将集合对象和遍历算法分离,使得代码更灵活、可扩展、易于维护。
  • 对客户端隐藏集合对象的内部实现,提高了代码的安全性。
  • 支持对同一种数据结构进行不同方式的遍历。

迭代器模式的缺点包括:

  • 需要实现迭代器对象和聚合对象,增加了代码复杂度。
  • 在集合内部元素发生变化时,需要及时更新迭代器状态,否则可能导致遍历结果不正确。

现在很多的开发语言都已经内置了迭代器模式,不要我们自己再去定义组件的迭代器(不能排除有特殊需求,还是得自己定义迭代器)

比如:

  1. 在 C# 中,集合类内部实现了 IEnumerableIEnumerator 接口,这两个接口就对应了迭代器模式中的抽象聚合类和抽象迭代器类。同时,C# 也提供了 yield 关键字,方便使用迭代器模式进行集合遍历。
  2. Python 中的 __iter__()__next__() 方法,Ruby 中的 each 方法等
  3. 在 Java 中,集合框架中的 Iterator 接口和相应的实现类就是迭代器模式的典型实现。同时,Java 8 还引入了基于 Lambda 表达式的 Stream API,它提供了非常便捷的集合遍历方式,并且可以进行各种数据处理和转换操作,进一步提高了代码的可读性、可维护性和可重用性。

更新迭代器状态,否则可能导致遍历结果不正确。

现在很多的开发语言都已经内置了迭代器模式,不要我们自己再去定义组件的迭代器(不能排除有特殊需求,还是得自己定义迭代器)

比如:

  1. 在 C# 中,集合类内部实现了 IEnumerableIEnumerator 接口,这两个接口就对应了迭代器模式中的抽象聚合类和抽象迭代器类。同时,C# 也提供了 yield 关键字,方便使用迭代器模式进行集合遍历。
  2. Python 中的 __iter__()__next__() 方法,Ruby 中的 each 方法等
  3. 在 Java 中,集合框架中的 Iterator 接口和相应的实现类就是迭代器模式的典型实现。同时,Java 8 还引入了基于 Lambda 表达式的 Stream API,它提供了非常便捷的集合遍历方式,并且可以进行各种数据处理和转换操作,进一步提高了代码的可读性、可维护性和可重用性。

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

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

相关文章

HydroD 实用教程(九)时域水动力分析

目 录 一、前言二、前处理三、定义/提交作业3.1 创建分析作业3.2 定义输入数据3.3 设置执行指令3.4 指定输出格式3.5 提交求解计算 四、输出文件五、结果后处理5.1 绘制力/位移时程5.2 傅里叶变换与导荷5.3 播放时域结果动画 六、参考文献 一、前言 SESAM &#xff08;Super El…

扫描仪连续扫描提示有一个问题阻值扫描该文档。请重试,错误的解决办法

故障现象: 用户新安装的联想M7650DNA一体多功能激光打印机,安装完所有驱动后;打印、复印都正常,只有扫描不正常,扫描多张后就会提示:有一个问题阻值扫描该文档。请重试,或者参阅“帮助和支持”或扫描仪附带的信息,了解有关疑难解答的信息。如下图:故障。 开始怀…

基于Pytest+Allure+Excel的接口自动化测试框架

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发…

Maven项目管理-随笔(入门)

目录 前言 什么是Maven Maven的优点 Maven的核心概念有哪些 POM是什么 什么是依赖管理 什么是插件 什么是仓库 概述 1、构建 2、依赖 安装与配置 1、下载 2、Windows Maven安装 1&#xff09;解压到指定目录 2&#xff09;配置环境变量 3&#xff09;目录结构 …

离散数学题目收集整理练习(期末过关进度60%)

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 ✨博主的其他文章&#xff1a;点击…

基于ssm框架的数字化题库与在线考试系统设计与实现+第二稿+文档

博主介绍&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 基于ssm框架的数字化题库与在线考试系统设计与实现第二稿文档 视频演示 视频去哪了呢&#xff1f;_哔哩哔哩_bilibili 系统介绍 摘 要 随着科学技术…

iOS游戏反外挂方案解析

自2007年iPhone OS发布以来&#xff0c;iOS系统已经发展了近17年&#xff0c;凭借着独家的系统环境、安全性更高的闭源生态等优势。iOS从一众手机系统中脱颖而出&#xff0c;与安卓稳坐手机系统市场两把头部交椅。 不同于安卓的开源生态&#xff0c;iOS的闭源生态中的硬件、软…

【计算机网络自顶向下】如何学好计网-第五章数据链路层

第五章 数据链路层 学习目的 目的1&#xff1a;理解链路层服务的主要功能 差错检查、纠错 共享广播信道&#xff1a;多点接入问题(multiple access) 链路层寻址(link layer addressing) 局域网技术&#xff1a;Ethernet, VLANs 目的2&#xff1a;链路层技术的实现 点到点…

Vue介绍与入门(一)

文章目录 前言一、Vue.js是什么&#xff1f;二、vue入门1. 引入vue.js2. 编写入门的简易代码&#xff08;实践&#xff09; 三、vue学习总结&#xff08;重点&#xff09; 前言 前端开发三大框架 1、Vue&#xff1a;尤雨溪主导开发 2、React&#xff1a;脸书&#xff08;Faceb…

MySQL 02:常用数据类型

<~生~信~交~流~与~合~作~请~关~注~公~众~号生信探索> 主要的数据类型&#xff0c;包括字符串、数值、日期时间 数值型 INT就是整数类型&#xff0c;根据允许的数值大小分为以下类型&#xff08;由小到大&#xff09;&#xff0c;这样做的目的是节约空间 INT类型范围&…

【Vscode 远程连接 Docker 容器】

文章目录 1. 配置docker镜像2. 安装 OpenSSH3. Vscode中安装 Remote-SSH 插件&#xff1a;4. 配置连接信息 1. 配置docker镜像 在主机目录下创建一个 Dockerfile&#xff0c;注意文件名必须保持一致&#xff01;&#xff01;&#xff01;&#xff08;默认装了docker&#xff09…

从零开始了解Redis 主从复制全部流程

主从复制 主从复制介绍 分析单个Redis 的问题 在一个项目中读的操作是比写的操作要多的 像京东&#xff0c;淘宝等等同一时刻看的人是远远多于买的人的所有单个redis既要承担写的操作又要承担读的操作效率低在高并发的情况下不稳定 所以引出了主从复制 一图胜千言 Redis …

数据库入门下篇(如何安装和登录MYSQL数据库)

在这篇文章里&#xff0c;笔者将着重讲解如何在win和Linux系统上安装自己的MySQL数据库软件&#xff0c;以及安装好数据库软件后如何启动和登录&#xff0c;忘了密码怎么办&#xff1f;如何创建一个数据库&#xff0c;如何在数据库中创建一个表等内容 目录 在windows系统上安装…

宠物行业 | 活动落地页设计指南基础版

中国是全球第二大宠物市场&#xff0c;同时也是增长最快的市场之一。随着养宠人群的扩大&#xff0c;人宠亲情关系的加深&#xff0c;客群消费意愿与消费水平的提高&#xff0c;中国宠物行业正处于消费与认知的全面升级期。 调研显示&#xff0c;2022年我国宠物产业规模达4936亿…

管理类联考——英语二——技巧篇——写作——图表作文——经典方法论

考研英语(二)的B节写作主要考查的是图表作文。笔者根据考研英源(二)大纲要求以及议论文经典的三段式写法(首段指出问题、中间段分析问题、尾段解决问题)&#xff0c;研发出一套图表作文的经典写法。下面我们来看图表作文经典的三段式写法的基本大招。 从上图可以看出&#xf…

【SpringCloud入门】-- Nacos快速入门之搭建服务与注册中心

目录 前言&#xff1a; 1.Nacos的下载与安装 2. 去MySQL建立一个名为nacos的数据库 3.介绍配置文件&#xff0c;conf目录下的 application.properties 4.nacos启动 5. nacos作为注册中心的作用 6.建立一个项目&#xff0c;实现向命名空间注册 前言&#xff1a; 上文我们已…

使用influxQL 查询influxDB 2.0以上版本

使用grafana 9.0 连接influxdb 2.0 时候,只能用FLux语言连接,就没有SQL编辑面板,通过研究搞定了,先看效果。 influxQL 格式连接 influxdb2.0 无法连接,总数报错 bad request. 那就用FLux格式连接,连接成功后,查询的地方没有可视化面板,只有编写脚本的地方,很不方便…

Android11 DNS解析流程

Android11 DNS解析 1. DNS解析概念 ​ DNS的全称是domain name system&#xff0c;即域名系统。主要目的是将域名解析为IP地址&#xff0c;域名是方便用户记忆&#xff0c;但网络传输中源目地址使用IP地址来进行标识的&#xff0c;所以Android中的网络应用程序在发起http请求…

MySQL redo log

redo log介绍 重做日志&#xff0c;用于记录事务操作的变化&#xff0c;确保事务的持久性。redo log是在事务开始后&#xff08;begin; 之后&#xff09;就开始记录&#xff0c;不管事务是否提交都会记录下来&#xff0c;在异常发生时&#xff08;如数据持久化过程中掉电&…

如何在 Vue3 组件中使用 TS 类型(必看)

一、为 props 标注类型 使用 <script setup> 方式一&#xff1a;当使用 <script setup> 时&#xff0c;defineProps() 宏函数支持从它的参数中推导类型&#xff1a; const props defineProps({treeTableProps: {type: Array,default: null,required: false},ms…