资讯专栏INFORMATION COLUMN

一起学设计模式 - 迭代器模式

JasonZhang / 3053人阅读

摘要:迭代器模式属于行为型模式的一种,提供一种方法访问一个容器中各个元素,而又不需要暴露该对象的内部细节。迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。

迭代器模式(Iterator Pattern)属于行为型模式的一种,提供一种方法访问一个容器中各个元素,而又不需要暴露该对象的内部细节。

概述

迭代器模式听起来可能感觉很陌生,但是实际上,迭代器模式是所有设计模式中最简单也是最常用的设计模式,正是因为太常用了,所以导致很多人忽略了它的存在。

在实际的开发过程中,我们可能需要针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但又不希望在聚合对象的抽象接口层中充斥着各种不同的便利操作。这个时候我们就需要这样一种东西,它应该具备如下三个功能:

能够便利一个聚合对象。

我们不需要了解聚合对象的内部结构。

能够提供多种不同的遍历方式。

迭代器模式: 把在元素之间游走的责任交给迭代器,而不是聚合对象。简化了聚合的接口和实现,让聚合更专注在它所应该专注的事情上,这样做更加符合单一责任原则。

UML结构图

模式结构

Iterator(抽象迭代器): 具体迭代器需要实现的接口,提供了游走聚合对象元素之间的方法

ConcreteIterator(具体迭代器): 对具体的聚合对象进行遍历,每一个聚合对象都应该对应一个具体的迭代器。

Aggregate(抽象聚合类): 存储和管理元素对象,声明了createIterator()用于创建一个迭代器对象,充当抽象迭代器工厂角色

ConcreteAggregate(具体聚合类): 实现了在抽象聚合类中声明的createIterator(),该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。

案例

UML图如下:

1.先定义抽象迭代器``

interface Iterator {

    //判断是否有还有下一个元素
    boolean hasNext();

    //取出下一个对象
    E next();
}

2.在定义一个具体迭代器对象,实现了Iterator中的方法

class MusicIterator implements Iterator {

    private E[] es;
    private int position = 0;

    public MusicIterator(E[] es) {
        this.es = es;
    }

    @Override
    public boolean hasNext() {
        return position != es.length;
    }

    @Override
    public E next() {
        E e = es[position];
        position += 1;
        return e;
    }
}

3.接下来定义抽象聚合类(常为 Collection , List , Set 等)

interface AbstractList {

    void add(E e);

    Iterator createIterator();
}

4.最后创建具体聚合类 (常为 ArrayList , HashSet 等,是抽象聚合类的实现类)

class MusicList implements AbstractList {

    private String[] books = new String[5];
    private int position = 0;


    @Override
    public void add(String name) {
        books[position] = name;
        position += 1;
    }

    @Override
    public Iterator createIterator() {
        return new MusicIterator<>(books);
    }
}

5.创建测试工程

public class Client {

    public static void main(String[] args) {
        AbstractList list = new MusicList();
        list.add("凉凉");
        list.add("奇谈");
        list.add("红颜");
        list.add("伴虎");
        list.add("在人间");
        Iterator iterator = list.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

}

6.运行效果

凉凉
奇谈
红颜
伴虎
在人间

至此一个简单的迭代器就完成了,实现了对容器的遍历。可以看到迭代器在Client中遍历时根本不需要知道他底层的实现,只需要通过迭代器来遍历就可以了。

JDK中应用

上文介绍了自己实现一个简单的迭代器,看完的应该就不陌生了,它其实在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。

List list = new ArrayList<>();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

看完这段代码是不是很熟悉,与我们上文代码基本类似

List:抽象聚合器

ArrayList:具体的聚合类

Iterator:抽象迭代器

list.iterator():返回的是实现了Iterator接口的具体迭代器对象

接下来看下ArrayList中的iterator是如何实现的

public Iterator iterator() {
    return new Itr();
}

private class Itr implements Iterator {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

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

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

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

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

这部分代码还是比较简单,大致就是在iterator方法中返回了一个实例化的Iterator对象。Itr是一个内部类,它实现了Iterator接口并实现了其中的方法。

当我们在使用JAVA开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现java.util.Iterable并实现其中的iterator方法使其返回一个java.util.Iterator的实现类就可以了。

总结

迭代器模式与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。但在绝大多数情况,我们是不需要自己实现的,基本都内置了

优点

支持不同方式遍历一个聚合对象,在同一个聚合上可以有多个遍历。

简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法。

在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码

缺点

由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

说点什么

全文代码:https://gitee.com/battcn/design-pattern/tree/master/Chapter14/battcn-iterator

个人QQ:1837307557

battcn开源群(适合新手):391619659

微信公众号:battcn(欢迎调戏)

福利

关注公众号:battcn,回复springboot即可获得 <2017最新spring boot 外卖实战微信公众平台视频教程>

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70670.html

相关文章

  • 一起设计模式 - 备忘录模式

    摘要:备忘录模式常常与命令模式和迭代子模式一同使用。自述历史所谓自述历史模式实际上就是备忘录模式的一个变种。在备忘录模式中,发起人角色负责人角色和备忘录角色都是独立的角色。 备忘录模式(Memento Pattern)属于行为型模式的一种,在不破坏封装特性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态。 概述 备忘录模式又叫做快照模式(...

    roland_reed 评论0 收藏0
  • Python要想得好,【容/可迭代对象/迭代/生成】少不了,稳扎稳打Python!

    摘要:总之,迭代器是有生成,可以通过进行调用。既然如此,我们在学基础的时候讲过是一个可迭代对象,那么它也是可以通过生成一个迭代器的。比如循环这种的,只要一执行就会把可迭代器里面的所有对象都获取。 ...

    li21 评论0 收藏0
  • 迭代模式

    摘要:迭代器模式的结构抽象容器一般是一个接口,提供一个方法,例如中的接口,接口,接口等。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。 定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。 类型:行为类模式。 类图:showImg(https://segmentfault.com/img/bVWQIQ?w=542&h=287);...

    xcc3641 评论0 收藏0
  • 练就Java24章真经—你所不知道的工厂方法

    摘要:用专业的话来讲设计模式是一套被反复使用多数人知晓的经过分类编目的代码设计经验的总结创建型模式,共五种工厂方法模式抽象工厂模式单例模式建造者模式原型模式。工厂方法模式的扩展性非常优秀。工厂方法模式是典型的解耦框架。 前言 最近一直在Java方向奋斗《终于,我还是下决心学Java后台了》,今天抽空开始学习Java的设计模式了。计划有时间就去学习,你这么有时间,还不来一起上车吗? 之所以要学...

    Andrman 评论0 收藏0
  • 练就Java24章真经—你所不知道的工厂方法

    摘要:用专业的话来讲设计模式是一套被反复使用多数人知晓的经过分类编目的代码设计经验的总结创建型模式,共五种工厂方法模式抽象工厂模式单例模式建造者模式原型模式。工厂方法模式的扩展性非常优秀。工厂方法模式是典型的解耦框架。 前言 最近一直在Java方向奋斗《终于,我还是下决心学Java后台了》,今天抽空开始学习Java的设计模式了。计划有时间就去学习,你这么有时间,还不来一起上车吗? 之所以要学...

    Chiclaim 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<