设计模式(18):迭代器模式

尺有所短,寸有所长;不忘初心,方得始终。

一、迭代器模式是什么

迭代器模式是针对集合对象而生的,对于集合对象肯定会有对集合的添加、删除、遍历集合等操作,基于单一职责原则,我们一般使用不同的类来承担不同的责任,迭代器模式就是用迭代器类来承担遍历集合的职责

定义】:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。属于行为型模式。

大白话:我们不需要知道集合里面放的是个啥,只需要把它按照顺序从集合中取出来就可以,比如我们在遍历Map的时候,我们并不关心Map里面的数据是什么类型,只需要通过next()方法取出即可。

主要作用】:遍历一个聚合对象。

解决的问题

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

在日常的开发中,以上代码基本上天天都在使用,以上这种循环遍历的方式需要依赖集合(arr)本身,i++每次循环自增1,就会迭代到下一元素,我们将循环变量(i)的作用抽象化、通用化就可以形成迭代器模式

二、迭代器模式的适用场景

【适用场景】

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

【生活案例】

  • Java集合Collection、List、Set、Map 等都包含了迭代器iterator

在日常开发中,我们基本上不会自己写迭代器,开源框架提供的 API 完全够用。除非自定义了一个数据结构,需要实现一个与之对应的迭代器。

三、迭代器模式结构

  • 抽象聚合(Aggregate)角色:声明添加、删除,遍历聚合对象的方法。遍历方法的返回类型必须被声明为迭代器接口【Iterator】, 这样具体集合就可以返回各种不同种类的迭代器。

    例如:Java中的Collection接口,List接口,Set接口等。

  • 具体聚合(Concrete Aggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。并持有一个存储集合数据的对象。

    例如:List接口的有序列表实现ArrayList,链表实现LinkedList。Set接口的哈希列表的实现HashSet等。

    • HashSet的iterator方法

      public Iterator<E> iterator() {
      return map.keySet().iterator();
      }
    • ArrayList的iterator方法

      public Iterator<E> iterator() {
      return new Itr();
      }
    • HashSet的HashMap

      public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
      {
      private transient HashMap<E,Object> map;
      //其他代码省略.......
      }
    • ArrayList的Object[]数组

      public class ArrayList<E> extends AbstractList<E>
      implements List<E>, RandomAccess, Cloneable, java.io.Serializable
      {
      transient Object[] elementData;
      //其他代码省略.......
      }
    • 持有一个存储集合数据的对象

    • 具体迭代器的实例

  • 抽象迭代器(Iterator)角色:声明访问和遍历聚合元素的接口,一般需要包含hasNext()、first()、next() 等方法。

  • 具体迭代器(Concrete iterator)角色:实现抽象迭代器接口,是遍历集合的一种特定算法,完成对聚合对象的遍历并记录遍历的当前位置。持有单个

    • 客户端 (Client)角色:通过集合和迭代器的接口与两者进行交互。设计模式(18):迭代器模式

四、迭代器模式实现方式

  • 声明迭代器接口,定义hasNext()、first()、next() 等方法
    • 声明具体迭代器接口,实现迭代器接口。通过迭代器的构造函数建立迭代器对象与单个集合实体链接。
  • 声明抽象聚合接口,定义获取迭代器的方法,其返回值必须是迭代器接口,以及集合的增加,删除等方法。
  • 声明具体聚合接口,实现抽象聚合接口。聚合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。
    • 客户端使用迭代器遍历集合。每当客户端需要遍历集合元素时都会获取一个新的迭代器。

五、迭代器模式的实现

【案例】:遍历一个公司的所有部门

【案例说明】:在此案例中,公司相当于是一个集合,里面储存部门,属于抽象聚合(Aggregate)角色,公司可以增加,删除部门。

  • 抽象聚合(Aggregate)角色

    /**
    * 抽象聚合(Aggregate)角色 : 公司
    */

    public interface CompanyAggregate {

    /**
    * 增加部门
    * @param obj
    */

    public void add(Object obj);
    /**
    * 删除部门
    * @param obj
    */

    public void remove(Object obj);

    /**
    * 获取一个具体的迭代器
    * @return
    */

    public CompanyIterator iterator();

    }
  • 具体聚合(Concrete Aggregate)角色

    /**
    * 具体聚合(Concrete Aggregate)角色 : 公司的行为实现
    */

    public class CompanyAggregateImpl implements CompanyAggregate{

    /**
    * 持有一个存储集合数据的对象
    */

    private List<Object> list ;

    public CompanyAggregateImpl() {
    this.list = new ArrayList<>();
    }

    @Override
    public void add(Object obj) {
    list.add(obj);
    }

    @Override
    public void remove(Object obj) {
    list.remove(obj);
    }

    /**
    *
    * 返回一个具体迭代器的实例 CompanyIterator
    * 集合对象将自身传递给迭代器的构造函数来创建两者之间的链接。
    * @return
    */

    @Override
    public CompanyIterator iterator() {
    return new CompanyConcreteIterator(list);
    }
    }
  • 抽象迭代器(Iterator)角色

    /**
    * 抽象迭代器(Iterator)角色 : 公司迭代器
    */

    public interface CompanyIterator {

    /**
    * 获取第一个元素
    * @return
    */

    Object first();
    /**
    * 获取下一个元素
    * @return
    */

    Object next();
    /**
    * 判断是否还有下一个元素,没有则遍历结束
    * @return
    */

    boolean hasNext();

    }
  • 具体迭代器(Concrete iterator)角色

    /**
    * 具体迭代器(Concrete iterator)角色 公司迭代器的具体实现
    */

    public class CompanyConcreteIterator implements CompanyIterator{

    /**
    * 记录遍历的当前位置
    */

    private int index;
    /**
    * 通过迭代器的构造函数建立迭代器对象与单个集合实体链接
    */

    private List<Object> list;
    public CompanyConcreteIterator(List<Object> list) {
    this.list = list;
    this.index = 0;
    }

    @Override
    public Object first() {
    return list.get(0);
    }

    @Override
    public Object next() {
    Object o = list.get(index);
    index++;
    return o;
    }

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

  • 客户端代码实现

        public static void main(String[] args) {

    /**
    * 获取集合对象
    */

    CompanyAggregate companyAggregate = new CompanyAggregateImpl();
    companyAggregate.add("信息科技部");
    companyAggregate.add("创新业务部");
    companyAggregate.add("行政部");
    companyAggregate.add("财务部");
    /**
    * 遍历
    * 在遍历的过程中iterator.next() 我们并不关心是集合内部是什么类型
    */

    CompanyIterator iterator = companyAggregate.iterator();
    while (iterator.hasNext()){
    Object next = iterator.next();
    System.out.println(next);
    }
    }
  • 案例输出结果

设计模式(18):迭代器模式

六、迭代器模式的优缺点

优点

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 遍历任务交由迭代器完成,简化了集合类,且满足单一职责原则
  • 增加新的聚合类和迭代器类都很方便,无须修改原有代码。且满足开闭原则
  • 支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历,满足封装的特性。

缺点

  • 增加了类的个数,增加了系统的复杂性。
  • 由于不知道内部结构,不能在遍历的同时更改集合中的元素。

七、迭代器模式和其他模式的区别

  • 可以使用【迭代器模式】来遍历【组合模式】树。
  • 可以同时使用【工厂方法模式】和[迭代器】来让子类集合返回不同类型的迭代器, 使迭代器与集合相匹配。
  • 可以同时使用【备忘录模式】和【迭代器】来获取当前迭代器的状态, 并且在需要的时候进行回滚。
  • 可以同时使用【访问者模式】和【迭代器】来遍历复杂数据结构, 并对其中的元素执行所需操作

八、总结

迭代器模式抽象迭代器类来分离了集合本身与对集合的遍历行为,在不暴露集合的内部结构的情况下让外部代码访问集合内部的数据


原文始发于微信公众号(星河之码):设计模式(18):迭代器模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/27107.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!