草庐IT

设计模式之迭代器模式

pluto_charon 2023-03-28 原文

迭代器模式又称游标模式,属于行为型模式;指提供一些方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表象。迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。

迭代器模式是最常见的几个设计模式之一,也是被广泛地应用到Java语言API中的几个设计模式之一。在Java语言的集合(Collection)框架中,广泛使用迭代器来遍历聚集的元素。

迭代器模式的UML类图如下:

从上图可知,迭代器模式涉及到抽象聚合角色、具体聚合角色、抽象迭代器角色、具体迭代器角色等四种角色:

  1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

迭代器分为主动迭代被动迭代,主要是相对于客户端而言的。如果客户端控制迭代的进程,那么这样的迭代就是主动迭代,相反就是被动迭代。使用主动迭代的客户端会明显调用迭代器的next()等迭代方法,在遍历的过程中向前迭代,而客户端在使用被动迭代时,明显不会调用迭代方法,而是由迭代器自行推进遍历过程。而我们在开发过程中大部分都是使用的主动迭代。

学校院系例子

在一个大学里,由很多个学院组成,每个学院下又有很多系。如果要知道这个大学由多少个学院,一个学院由多少个专业,就可以使用迭代器模式。

该系统的UML类图如下:

抽象聚合角色:

package com.charon.iterator;

import java.util.Iterator;

/**
 * @className: College
 * @description: 学院
 * @author: charon
 * @create: 2022-03-27 14:52
 */
public interface College {

    /**
     * 获取学院的名称
     * @return
     */
    String getName();

    /**
     * 添加学院
     * @param name 名称
     * @param desc 描述
     */
    void addDepartment(String name,String desc);

    /**
     * 返回一个迭代器对象,用于遍历
     * @return
     */
    Iterator createIterator();
}

具体聚合角色:

package com.charon.iterator;

import java.util.Iterator;

/**
 * @className: ComputerCollege
 * @description: 计算机学院
 * @author: charon
 * @create: 2022-03-27 14:56
 */
public class ComputerCollege implements College{

    Department[] departments;

    /**
     * 下面系的个数
     */
    int departmentNum;

    public ComputerCollege() {
        departments = new Department[3];
        addDepartment("计算机科学与技术","计算机科学与技术");
        addDepartment("电子科学与技术","电子科学与技术");
        addDepartment("信息与通信工程","信息与通信工程");
    }

    @Override
    public String getName() {
        return "计算机学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        Department department = new Department(name, desc);
        departments[departmentNum] = department;
        departmentNum++;
    }

    @Override
    public Iterator createIterator() {
        return new ComputerCollegeIterator(departments);
    }
}


package com.charon.iterator;

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

/**
 * @className: InfoCollege
 * @description: 信息工程学院
 * @author: charon
 * @create: 2022-03-27 15:11
 */
public class InfoCollege implements College{

    List<Department> departments;

    public InfoCollege() {
        departments = new ArrayList<>(5);
        addDepartment("电气工程及自动化","电气工程及自动化");
        addDepartment("电子信息工程","电子信息工程");
        addDepartment("测控技术与仪器","测控技术与仪器");
        addDepartment("电子科学与技术","电子科学与技术");
        addDepartment("物联网工程","物联网工程");
        addDepartment("通信工程","通信工程");
    }

    @Override
    public String getName() {
        return "信息工程学院";
    }

    @Override
    public void addDepartment(String name, String desc) {
        Department department = new Department(name, desc);
        departments.add(department);
    }

    @Override
    public Iterator createIterator() {
        return new InfoCollegeIterator(departments);
    }
}

具体迭代器角色:

package com.charon.iterator;

import java.util.Iterator;

/**
 * @className: ComputerCollegeIterator
 * @description:
 * @author: charon
 * @create: 2022-03-27 15:05
 */
public class ComputerCollegeIterator implements Iterator {

    /**
     * 存放专业的容器
     */
    Department[] departments;

    /**
     * 游标,用于记录遍历的位置
     */
    int cursor;

    public ComputerCollegeIterator(Department[] departments) {
        this.departments = departments;
    }

    @Override
    public boolean hasNext() {
        return cursor < departments.length && departments[cursor] != null;
    }

    @Override
    public Object next() {
        Department department = departments[cursor];
        cursor++;
        return department;
    }
}


package com.charon.iterator;

import java.util.Iterator;
import java.util.List;

/**
 * @className: InfoCollegeIterator
 * @description:
 * @author: charon
 * @create: 2022-03-27 15:16
 */
public class InfoCollegeIterator implements Iterator {

    List<Department> departments;
    /**
     * 数组下标
     */
    int index;

    public InfoCollegeIterator(List<Department> departments) {
        this.departments = departments;
    }

    @Override
    public boolean hasNext() {
        if (index >= departments.size() -1){
            return false;
        }
        index++;
        return true;
    }

    @Override
    public Object next() {
        return departments.get(index);
    }
}

其他:

package com.charon.iterator;

/**
 * @className: Department
 * @description: 系
 * @author: charon
 * @create: 2022-03-27 14:58
 */
public class Department {

    private String name;

    private String desc;

    public Department(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    /**
     * Gets the value of name
     *
     * @return the value of name
     */
    public String getName() {
        return name;
    }
}


package com.charon.iterator;

import java.util.Iterator;
import java.util.List;

/**
 * @className: OutputImpl
 * @description:
 * @author: charon
 * @create: 2022-03-27 15:19
 */
public class OutputImpl {

    List<College> colleges;

    public OutputImpl(List<College> colleges) {
        this.colleges = colleges;
    }

    /**
     * 遍历所有的学院,并依次输出
     */
    public void printCollege(){
        Iterator<College> iterator = colleges.iterator();
        while (iterator.hasNext()){
            // 取出一个学院
            College college = iterator.next();
            System.out.println("学院:" + college.getName());
            printDepartment(college.createIterator());
        }
    }

    /**
     * 输出专业
     * @param iterator
     */
    private void printDepartment(Iterator iterator) {
        while (iterator.hasNext()){
            Department department = (Department) iterator.next();
            System.out.println("该学院下的专业有:" + department.getName());
        }
    }
}

测试:

package com.charon.iterator;

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

/**
 * @className: Client
 * @description:
 * @author: charon
 * @create: 2022-03-27 15:27
 */
public class Client {

    public static void main(String[] args) {
        // 创建学院
        List<College> colleges = new ArrayList<>(2);
        
        colleges.add(new ComputerCollege());
        colleges.add(new InfoCollege());

        OutputImpl output = new OutputImpl(colleges);
        output.printCollege();
    }
}

打印:
    学院:计算机学院
    该学院下的专业有:计算机科学与技术
    该学院下的专业有:电子科学与技术
    该学院下的专业有:信息与通信工程
    学院:信息工程学院
    该学院下的专业有:电子信息工程
    该学院下的专业有:测控技术与仪器
    该学院下的专业有:电子科学与技术
    该学院下的专业有:物联网工程
    该学院下的专业有:通信工程

迭代器模式的优点如下:

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。
  2. 遍历任务交由迭代器完成,这简化了聚合类。
  3. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

迭代器模式的缺点如下:

  1. 增加了类的个数,这在一定程度上增加了系统的复杂性

迭代器模式的应用场景

由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。

迭代器模式通常在以下几种情况使用。

  1. 当需要为聚合对象提供多种遍历方式时。
  2. 当需要为遍历不同的聚合结构提供一个统一的接口时。
  3. 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

有关设计模式之迭代器模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

    我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

  6. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  10. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

随机推荐