[设计模式Java实现附plantuml源码~行为型]遍历聚合对象中的元素——迭代器模式

news2024/11/17 2:53:53

前言:
为什么之前写过Golang 版的设计模式,还在重新写Java 版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。


🔥[设计模式Java实现附plantuml源码]专链

  • 创建型
  1. 确保对象的唯一性~单例模式
  2. 集中式工厂的实现~简单工厂模式
  3. 多态工厂的实现——工厂方法模式
  4. 产品族的创建——抽象工厂模式
  5. 对象的克隆~原型模式
  6. 复杂对象的组装与创建——建造者模式
  • 结构型
  1. 提供统一入口——外观模式
  2. 扩展系统功能——装饰模式
  3. 树形结构的处理——组合模式
  4. 对象的间接访问——代理模式
  5. 不兼容结构的协调——适配器模式
  6. 处理多维度变化——桥接模式
  7. 实现对象的复用——享元模式
  • 行为型
  1. 请求的链式处理——职责链模式

  2. 请求发送者与接收者解耦——命令模式

  3. 遍历聚合对象中的元素——迭代器模式


文章目录

      • 简单代码实现
      • 使用内部类实现迭代器
        • 简单代码使用内部类实现
      • `JDK`内置的迭代器
      • 总结
        • 主要优点
        • 主要缺点
      • 适用场景


在软件开发时,经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据;二是遍历数据。从依赖性来看,前者是聚合对象的基本职责;而后者既是可变化的,又是可分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中。由迭代器来提供遍历聚合对象内部数据的行为,这将简化聚合对象的设计,更符合单一职责原则的要求。

迭代器模式定义如下:
迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。

在迭代器模式结构中包含聚合和迭代器两个层次结构。考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式,其模式结构如图所示。
在这里插入图片描述

@startuml

interface Iterator {
+ first()
+ next()
+ hasNext()
+ currentItem()
}

class ConcreteIterator implements Iterator {
+ first()
+ next()
+ hasNext()
+ currentItem()
}

abstract class Aggregate {
+ createIterator()
}

class ConcreteAggregate extends Aggregate {
+ createIterator()
}

ConcreteAggregate .right.> ConcreteIterator: 创建迭代器

ConcreteIterator -left-> ConcreteAggregate: aggregate

@enduml

由图可以看出,在迭代器模式结构图中包含以下4个角色。
(1)Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法。例如,用于获取第一个元素的 first() 方法,用于访问下一个元素的 next() 方法,用于判断是否还有下一个元素的 hasNext() 方法,用于获取当前元素的 currentItem() 方法等。在具体迭代器中将实现这些方法。
(2)ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置。在具体实现时,游标通常是一个表示位置的非负整数。
3)Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个 createIterator() 方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
(4)ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的 createIterator() 方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。


在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历。迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。

简单代码实现

package behavior;

import java.util.ArrayList;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        ConcreteAggregate aggregate = new ConcreteAggregate(List.of("a", "b", "c"));
        Iterator<String> iterator = aggregate.createIterator();
        System.out.println(iterator.first());
        System.out.println(iterator.next());
        System.out.println(iterator.hasNext());
        System.out.println(iterator.currentItem());
        System.out.println(iterator.next());
        System.out.println(iterator.hasNext());
        System.out.println(iterator.next());

    }
    //定义迭代器接口
    public interface Iterator<T> {
        T first();
        T next();
        boolean hasNext();
        T currentItem();
    }

    //定义具体迭代器
    public static class ConcreteIterator implements Iterator<String> {

        private List<String> list = new ArrayList<>();
        private int index = 0;

        public ConcreteIterator(List<String> list){
            this.list = list;
        }

        @Override
        public String first() {
            index = 0;
            return list.get(index);
        }

        @Override
        public String next() {
            if(hasNext()){
                return list.get(++index);
            }
            return null;
        }

        @Override
        public boolean hasNext() {
            return index < list.size()-1;
        }

        @Override
        public String currentItem() {
            return list.get(index);
        }
    }

    //定义集合接口
    public static abstract class Aggregate {
        public abstract Iterator<String> createIterator();
    }

    //定义具体集合
    public static class ConcreteAggregate extends Aggregate {

        private List<String> list = new ArrayList<>();

        public ConcreteAggregate(List<String> list){
            this.list = list;
        }

        @Override
        public Iterator<String> createIterator() {
            return new ConcreteIterator(list);
        }
    }
}

输出结果

a
b
true
b
c
false
null

使用内部类实现迭代器

在前图所示的迭代器模式结构图中可以看到,具体迭代器类和具体聚合类之间存在双重关系,其中一个关系为关联关系。在具体迭代器中需要维持一个对具体聚合对象的引用,该关联关系的目的是访问存储在聚合对象中的数据,以便迭代器能够对这些数据进行遍历操作。
除了使用关联关系外,为了能够让迭代器可以访问到聚合对象中的数据,还可以将迭代器类设计为聚合类的内部类。JDK中的迭代器类就是通过这种方法来实现的。
其中AbstractList类的代码片段如下:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    ...
    public Iterator<E> iterator() {
        return new Itr();
    }
    
     private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException(e);
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
}
简单代码使用内部类实现
package behavior;

import java.util.List;

public class IteratorDemo {

    public static void main(String[] args) {
        ConcreteAggregate aggregate = new ConcreteAggregate(List.of("a", "b", "c"));
        Iterator<String> iterator = aggregate.createIterator();
        System.out.println(iterator.first());
        System.out.println(iterator.next());
        System.out.println(iterator.hasNext());
        System.out.println(iterator.currentItem());
        System.out.println(iterator.next());
        System.out.println(iterator.hasNext());
        System.out.println(iterator.next());
    }

    public interface Iterator<T> {
        T first();
        T next();
        boolean hasNext();
        T currentItem();
    }

    public abstract static class Aggregate {
        public abstract Iterator<String> createIterator();
    }

    public static class ConcreteAggregate extends Aggregate {
        private final List<String> list;

        public ConcreteAggregate(List<String> list) {
            this.list = list;
        }

        @Override
        public Iterator<String> createIterator() {
            return new ConcreteIterator();
        }

        private class ConcreteIterator implements Iterator<String> {
            private int index = 0;

            @Override
            public String first() {
                index = 0;
                return list.get(index);
            }

            @Override
            public String next() {
                if (hasNext()) {
                    return list.get(++index);
                }
                return null;
            }

            @Override
            public boolean hasNext() {
                return index < list.size() - 1;
            }

            @Override
            public String currentItem() {
                return list.get(index);
            }
        }
    }
}

JDK内置的迭代器

public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

总结

迭代器模式是一种使用频率非常高的设计模式,通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来。聚合对象只负责存储数据,而遍历数据由迭代器来完成。由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,只需要直接使用Java、C#等语言已定义好的迭代器即可。迭代器已经成为操作聚合对象的基本工具之一。

主要优点

迭代器模式的主要优点如下:
(1)支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,也可以自己定义迭代器的子类以支持新的遍历方式。
(2)迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
(3)在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足开闭原则的要求。

主要缺点

迭代器模式的主要缺点如下:
(1)由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
(2)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展。例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

适用场景

在以下情况下可以考虑使用迭代器模式:
(1)访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
(2)需要为一个聚合对象提供多种遍历方式。
(3)为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。



🚀 作者简介:作为某云服务提供商的后端开发人员,我将在这里与大家简要分享一些实用的开发小技巧。在我的职业生涯中积累了丰富的经验,希望能通过这个博客与大家交流、学习和成长。技术栈:Java、Golang、PHP、Python、Vue、React


本文收录于三木的
💐 「设计模式」专栏
此外三木还有以下专栏在同步更新~

🌼 「AI」专栏

🔥「面试」这个专栏的灵感来自于许多粉丝私信,大家向我咨询有关面试的问题和建议。我深感荣幸和责任,希望通过这个专栏,能够为大家提供更多关于面试的知识、技巧和经验。我们将一起探讨面试。期待粉丝们ssp的offer喜讯。

🎈 「Java探索者之路」系列专栏,这个专栏旨在引领Java开发者踏上一段真正探索Java世界的旅程。
我们将深入探讨Java编程的方方面面,从基础知识到高级技巧,从实践案例到最新趋势,帮助你成为一名卓越的Java探索者。如果有想进入Java后端领域工作的同学,这个专栏会对你有所帮助,欢迎关注起来呀

🌊 「Python爬虫」的入门学习系列,大家有兴趣的可以看一看


🌹一起学习,互三互访,顺评论区有访必回,有关必回!!!


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

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

相关文章

ElasticSearch语法

Elasticsearch 概念 入门学习: Index索引>MySQL 里的表(table)建表、增删改查(查询需要花费的学习时间最多)用客户端去调用 ElasticSearch(3 种)语法:SQL、代码的方法(4 种语法) ES 相比于 MySQL&#xff0c;能够自动帮我们做分词&#xff0c;能够非常高效、灵活地查询内…

基于springboot+vue的教学资源库系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Java的编程之旅19——使用idea对面相对象编程项目的创建

在介绍面向对象编程之前先说一下我们在idea中如何创建项目文件 使用快捷键CtrlshiftaltS新建一个模块&#xff0c;点击“”&#xff0c;再点New Module 点击Next 我这里给Module起名叫OOP,就是面向对象编程的英文缩写&#xff0c;再点击下面的Finish 点Apply或OK均可 右键src…

嵌入式Qt 计算器核心算法_1

一.表达式分离算法分析 二.分离算法实现 QCalculatorDec.cpp #include "QCalculatorDec.h"#include <QDebug>QCalculatorDec::QCalculatorDec() {m_exp "";m_result "";QQueue<QString> r split("9.11 ( -3 - 1 ) * -5 &…

SpringCloud Ribbon负载均衡的策略总结及其配置

1. 轮询策略 2. 权重轮询策略 3. 随机策略 4. 最少并发数策略 5. 在选定的负载均衡策略基础上重试机制 6. 可用性敏感策略。 7. 区域敏感策略 —————————————————————— Ribbon负载均衡策略的配置&#xff1a; 在application.yml中配置如下&am…

LeetCode | 两数相加 C语言

Problem: 2. 两数相加 文章目录 思路解题方法Code一些感想 思路 主要是一一相加和逆序的方式存储 先说逆序储存&#xff0c;看下图 我们先声明出指针p和指针q&#xff0c;还有指针head&#xff08;主要用于return上而已&#xff09;&#xff0c;然后进行一系列操作&#xff0c…

Disentangled Transfer Learning for Visual Recognition

DTL: Disentangled Transfer Learning for Visual Recognition 论文链接&#xff1a;https://arxiv.org/pdf/2312.07856.pdf源码链接&#xff1a;https://github.com/heekhero/DTL 简介 大规模预训练加微调的框架已经在各个领域推广。但是由于GPU内存或时间预算的原因&#…

Sora:打开视频创作新纪元的魔法钥匙

随着人工智能技术的飞速发展&#xff0c;AI视频模型已成为科技领域的新热点。而在这个浪潮中&#xff0c;OpenAI推出的首个AI视频模型Sora&#xff0c;以其卓越的性能和前瞻性的技术&#xff0c;引领着AI视频领域的创新发展。让我们将一起探讨Sora的技术特点、应用场景以及对未…

ctfshow web入门 web141-145

1.web141 ^\w$表示在开头和末尾匹配字母数字_&#xff0c;传入的v3值不能有字母数字_&#xff0c;即无字母的命令执行 php中1-phpinfo()是可以执行的&#xff0c;加减乘除都可以实现 这里或&#xff0c;异或&#xff0c;取反等运算都可以 这里采用羽师傅的异或脚本生成paylo…

代码随想录第二十三天 回溯算法 77.组合 216.组合总和 17.电话号码的字母组合

回溯算法 LeetCode 77 组合 题目描述 思路 递归函数的返回值以及参数 在这里要定义两个全局变量&#xff0c;一个用来存放符合条件单一结果&#xff0c;一个用来存放符合条件结果的集合。 代码如下&#xff1a; vector<vector<int>> result; // 存放符合条件…

如何使用Docker本地部署Jupyter+Notebook容器并结合内网穿透实现远程访问

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

思科Cisco下输入问号“?”命令help

Cisco CLI &#xff08;Command-Line Interface&#xff09;命令下输入问号“&#xff1f;” 一、在Cisco CLI下&#xff0c;“&#xff1f;”被认为是 – help的帮助命令&#xff0c;当在默认情况下输入“&#xff1f;”&#xff0c;系统会自动识别为 help 命令。 二、那么&a…

【Java面试系列】Nginx

目录 为什么要用Nginx&#xff1f;为什么Nginx性能这么高&#xff1f;Nginx 是如何实现高并发的&#xff1f; Nginx怎么处理请求的&#xff1f;Nginx的工作流程 给 favicon.ico 和 robots.txt 设置过期时间; 这里为 favicon.ico 为 99 天,robots.txt 为 7 天并不记录 404 错误日…

牛B了,GitHub用户免费领空投: TOP 5k 项目的贡献者可领取价值 $200空投。

StarkNet 公链项目为了激励开发者参与其平台建设&#xff0c;启动了空投活动。 如果曾向 GitHub 上获得较多 Star 的项目提交过 PR &#xff0c;就有资格领取 111.1 STRK 的空投奖励。 只需要使用 OAuth 2.0 登录&#xff0c;就可以直接领取。 领取规则 截止到 2023 年 11 月 …

【图片公式识别】图片公式转Word与LaTeX文档:智能识别与转换

前言 嘿&#xff0c;大家好呀&#xff01;&#x1f44b; 谁都知道&#xff0c;写 Word 文档里的公式可不是一件简单的事情&#xff01;你辛辛苦苦在键盘上敲出的数学公式&#xff0c;结果随着 Word 版本的更新&#xff0c;竟然变成了一张图片&#xff01;&#x1f624; 这简直就…

客户端web开发工具

文章目录 安全网络Linter-->捕获代码错误-->eslint源代码控制-->Git代码格式化-->Prettier打包工具--Parcel--Webpack 转换--Babel开发后阶段测试工具配置工具其他 node&#xff0c;npm、yarnnode.js包管理器npmyarn https://developer.mozilla.org/zh-CN/docs/Lea…

http和https的区别(简述)

HTTP&#xff08;HyperText Transfer Protocol&#xff09;和HTTPS&#xff08;HTTP Secure&#xff09;都是用于在客户端和服务器之间传输数据的协议&#xff0c;但它们在安全性方面有重要的区别。 1.HTTP: 概述&#xff1a; HTTP是一种用于传输超文本的协议&#xff08;超文…

洗选中心智能化运维工是做什么的?智能化运维工程师是干什么的

洗选中心智能化运维工程师的职责和工作内容&#xff1f;同时&#xff0c;描述智能化运维工程师在信息技术行业中的具体角色和他们的主要任务。  洗选中心智能运维工程师的职责和工作内容主要包括&#xff1a;  设备监控管理&#xff1a;重点对洗涤中心机器进行实时监控管理…

新书速览|细说PyTorch深度学习:理论、算法、模型与编程实现

超详细的PyTorch深度学习入门书&#xff0c;100余个编程示例6大热点案例&#xff0c;大咖带路&#xff0c;边学边实践。 本书特点&#xff1a; 1. 专家编撰&#xff1a;由资深专家精心编撰&#xff0c;通俗易懂&#xff0c;娓娓道来 2&#xff0e;范例丰富&#xff1a;100余个…

ChatGPT在数据分析OKR计划中的应用

ChatGPT在数据分析OKR计划中的应用 ​ 现在大多数公司引入了OKR&#xff08;objective & key result&#xff09;计划&#xff0c;数据分析师也需要定期制定和检查自己的OKR计划。我们不仅可以利用ChatGPT辅助制定OKR计划&#xff0c;也可以让其对OKR计划内容进行调整、优…