乐趣区

关于后端:设计模式之迭代器模式

本文介绍设计模式中的迭代器模式,首先艰深的解释迭代器模式的基本概念和对应的四个角色,并依据四个角色举一个典型的实例,为了增强常识的连贯性,咱们以 Jdk 源码汇合中应用迭代器模式的利用进一步阐明,最初阐明迭代器模式的利用场景和优缺点。

读者能够拉取残缺代码本地学习,实现代码均测试通过上传到码云

一、概念了解

迭代器模式官网解释就是提供一个对象来程序拜访聚合对象中的一系列数据,而不裸露聚合对象的外部示意。何为聚合对象呢?最典型的就是汇合类。

大白话也就是,汇合中的数据是公有的,汇合中不应该提供间接遍历的办法,要定义一个新的对象用于拜访这个汇合。

既然是一个专门用来遍历的对象,一个被遍历的聚合对象,很显然至多有两个对象,迭代器对象、聚合对象,因为遵循面向接口编程的准则,迭代器对象和聚合对象应该形象进去接口,那自然而然就是应该有四个角色:

形象聚合(InterfaceAggregate)角色:定义存储、增加、删除聚合元素以及创立迭代器对象的接口。

具体聚合(ConcreteAggregate)角色:实现形象聚合类,返回一个具体迭代器的实例。

形象迭代器(Iterator)角色:定义拜访和遍历聚合元素的接口,通常蕴含 hasNext()、next() 等办法。

具体迭代器(Concretelterator)角色:实现形象迭代器接口中所定义的办法,实现对聚合对象的遍历,记录遍历的以后地位。

基于四个角色咱们举一个典型案例。

二、案例实现

应该是有四个类

形象聚合角色,用于定义增删改查元素的对立标准接口,和创立迭代器对象的办法

具体聚合角色,实现形象聚合角色办法

形象迭代器角色,定义遍历元素的对立标准接口

具体迭代器,实现形象迭代器角色的办法。

形象聚合角色:

/**
 * 形象聚合角色
 * @author tcy
 * @Date 13-09-2022
 */
public interface InterfaceAggregate {
    /**
     * 减少对象
     * @param obj 对象
     */
    void add(Object obj);

    /**
     * 移除对象
     * @param obj 对象
     */
    void remove(Object obj);

    /**
     * 调用迭代器
     * @return 迭代器
     */
    Iterator getIterator();}

具体聚合角色:

/**
 * 具体聚合角色
 * @author tcy
 * @Date 13-09-2022
 */
public class ConcreteAggregate implements InterfaceAggregate{private List<Object> list = new ArrayList<>();
    @Override
    public void add(Object obj) {list.add(obj);
    }

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

    @Override
    public Iterator getIterator() {return new Concretelterator(list);
    }
}

形象迭代器角色:

/**
 * 形象迭代器
 * @author tcy
 * @Date 13-09-2022
 */
public interface Iterator<E> {

    /**
     * 删除对象
     * @return 对象
     */
    Object remove();

    /**
     * 调用下一个对象
     * @return 对象
     */
    E next();

    /**
     * 迭代器中是否还有下一个对象
     * @return
     */
    boolean hasNext();

    /**
     * 遍历迭代器中残余的对象
     * @param action
     */
    default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }

}

具体迭代器角色:

/**
 * 具体迭代器角色
 * @author tcy
 * @Date 13-09-2022
 */
public class Concretelterator implements Iterator{
    private List<Object> list = null;
    private int index = -1;

    public Concretelterator(List<Object> list) {this.list = list;}

    @Override
    public Object remove() {index = list.size();
        Object obj = list.get(index);
        list.remove(obj);
        return obj;
    }

    @Override
    public Object next() {
        Object obj = null;
        if (this.hasNext()) {obj = list.get(++index);
        }
        return obj;
    }

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

客户端调用:

/**
 * @author tcy
 * @Date 13-09-2022
 */
public class Client {public static void main(String[] args) {ConcreteAggregate concreteAggregate=new ConcreteAggregate();
        concreteAggregate.add("老王");
        concreteAggregate.add("小王");
        concreteAggregate.add("小张");

        System.out.println("Aggregate 聚合对象有:");

        Iterator iterator=concreteAggregate.getIterator();

        while (iterator.hasNext()){Object next = iterator.next();
            System.out.println(next.toString());
        }
        // 遍历剩下的角色
        iterator.forEachRemaining(ele -> System.out.println(ele));

    }

}

迭代器实现逻辑比拟清晰,了解起来难度也不大,理解了该设计模式,趁热打铁看迭代器模式在源码中的利用。

三、源码利用

迭代器模式在 Jdk 中的汇合类中有着宽泛的利用,咱们以 ArrayList 作为典型。

在 ArrayList 实现迭代器时,同样是有四个角色。

List 形象聚合类;

ArrayList 具体聚合角色;

Iterator 形象迭代器;

ArrayList 外部类 Itr 是具体迭代器;

咱们能够看到 ArrayList 是把具体聚合角色和具体迭代器都写在一个类中,Itr 作为具体迭代对象是以内部类的模式。

ArrayList 其实和咱们案例中的办法长的很像,只不过 ArrayList 中定义了更多的办法,而且 ArrayList 还有一个外部类 ListItr。

其实是迭代器的增强版,在继承 Itr 的根底之上实现 ListIterator 接口。

Iterator 迭代器除了,hasNext()、next()、remove() 办法以外,还有一个特地的 forEachRemaining() 办法,咱们重点说下 forEachRemaining() 办法,该办法代表的意思是遍历剩下的汇合。

比方咱们曾经调用了该汇合中的第一个元素,那么遍历时候就会主动疏忽第一个元素,遍历剩下的元素。

咱们写一个测试方法看成果:

public class Client {public static void main(String[] args) {
      
        // jdk ArrayList 迭代器
        // 创立一个元素类型为 Integer 的汇合
        Collection<String> collection =  new ArrayList<>();

            // 向汇合中增加元素
            collection.add("老王");
            collection.add("小王");
            collection.add("小张");

        // 获取该汇合的迭代器
        java.util.Iterator<String> iteratorJdk= collection.iterator();
        System.out.println("Arraylist 聚合对象有:");
        // 调用迭代器的通过汇合实现的形象办法遍历汇合元素
        while(iteratorJdk.hasNext())
        {System.out.println(iteratorJdk.next());
        }
        // 调用 forEachRemaining() 办法遍历汇合元素
        iteratorJdk.forEachRemaining(ele -> System.out.println(ele));

    }

}
Arraylist 聚合对象有:老王
小王
小张 

失常状况下,会打印两次汇合对象中的信息,实际上只打印了一次,正是因为 next 调用过的元素,forEachRemaining 不会再调。

看到这,想必你对迭代器曾经有了初步的理解,当在遍历元素时,除了应用 for 循环遍历元素以外,提供了另外一种形式遍历元素。

案例很好了解,源码中的利用也看得懂,然而理论开发中迭代器对象什么时候用呢?想必大部分人并不是很清晰。

接着看迭代器对象的利用场景和优缺点,看从中能不能找到答案。

四、总结

当一个对象是一个聚合对象且须要对外提供遍历办法时,能够应用迭代器模式,也即理论业务中定义的有聚合对象,外面寄存了咱们须要的业务数据,为了让业务数据的职责更清晰,咱们就能够将编辑的办法提取进去,另外定义一个迭代器对象用于遍历数据。

迭代器形式提供了不同的形式遍历聚合对象,减少新的聚合类和迭代器类都是比拟不便的,Java 汇合类中宏大的家族采纳迭代器模式就是基于这种长处。

迭代器模式有设计模式的通用毛病——零碎复杂性,迭代器模式将数据存储和数据遍历离开,减少了类的个数。

我曾经间断更新了十几篇设计模式博客,举荐你一起学习。

一、设计模式概述

二、设计模式之工厂办法和形象工厂

三、设计模式之单例和原型

四、设计模式之建造者模式

五、设计模式之代理模式

六、设计模式之适配器模式

七、设计模式之桥接模式

八、设计模式之组合模式

九、设计模式之装璜器模式

十、设计模式之外观模式

十一、外观模式之享元模式

十二、设计模式之责任链模式

十三、设计模式之命令模式

十四、设计模式之解释器模式

退出移动版