目 录CONTENT

文章目录

迭代器模式

半糖
2024-08-28 / 0 评论 / 0 点赞 / 8 阅读 / 12974 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-08-28,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

名称

迭代器模式(ITERATOR),别名:游标(Cursor)

目的

提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。

适用性

迭代器模式可用来:

  • 访问一个聚合对象的内容而无需暴露它的内部表示。

  • 支持对聚合对象的多种遍历。

  • 为遍历不同的聚合结构提供一个统一的接口 (即, 支持多态迭代)。

结构

迭代器(Iterator):迭代器定义访问和遍历元素的接口。

具体迭代器(ConcreteIterator)

  • 具体迭代器实现迭代器接口。

  • 对该聚合遍历时跟踪当前位置。

聚合(Aggregate):聚合定义创建相应迭代器对象的接口。

具体聚合(ConcreteAggregate):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。

协作

ConcreteIterator跟踪聚合中的当前对象,并能够计算出待遍历的后继对象。

效果

优点

  1. 支持以不同的方式(比如正向、反向或者特定条件下的遍历)遍历一个聚合。仅需用一个不同的迭代器的实例代替原先的实例即可。

  2. 迭代器简化了聚合的接口。有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了,这样就简化了聚合的接口。

  3. 在同一个聚合上可以有多个遍历。每个迭代器保持它自己的遍历状态,可以同时进行多个遍历。

  4. 封装遍历逻辑。迭代器模式封装了遍历逻辑,使得客户端代码不需要关心聚合对象的内部结构。

  5. 统一的访问接口。为遍历聚合对象提供了一个统一的接口,使得客户端能够一致地访问不同的聚合对象。

  6. 职责分离。将遍历逻辑从聚合对象本身分离出来,使得聚合对象专注于自身的职责,即存储和管理对象。

  7. 易于扩展。当需要添加新的遍历方式时,可以通过添加新的迭代器类来实现,而不需要修改现有的聚合类。

缺点

  1. 增加系统的复杂性。为了支持迭代器模式,需要引入额外的类(迭代器类),这会增加系统的复杂性。

  2. 潜在的并发问题。如果迭代器与聚合对象之间的状态不一致,可能会导致并发访问的问题。

  3. 额外的开发工作量。每当新增加一个聚合类时,可能都需要对应地增加一个新的迭代器类,增加了开发工作量。

  4. 资源消耗。迭代器的创建和维护可能会带来一定的资源开销,尤其是在处理大量数据的情况下。

实现

  1. 谁控制该迭代。一个基本的问题是决定由哪一方来控制该迭代,是迭代器还是使用该迭代器的客户。当由客户来控制迭代时,该迭代器称为一个外部迭代器(externaliterator),而当由迭代器控制迭代时,该迭代器称为一个内部迭代器(internaliterator)。使用外部迭代器的客户必须主动推进遍历的步伐,显式地向迭代器请求下一个元素。相反地,若使用内部迭代器,客户只需向其提交一个待执行的操作,而迭代器将对聚合中的每一个元素实施该操作。

    http://bantang.xyz/archives/wai-bu-die-dai-qi-he-nei-bu-die-dai-qi

  2. 谁定义遍历算法。迭代器不是唯一可定义遍历算法的地方。聚合本身也可以定义遍历算法,并在遍历过程中用迭代器来存储当前迭代的状态。我们称这种迭代器为一个游标(cursor),因为它仅用来指示当前位置。客户会以这个游标为一个参数调用该聚合的Next操作,而Next操作将改变这个指示器的状态。
    如果迭代器负责遍历算法,那么将易于在相同的聚合上使用不同的迭代算法,同时也易于在不同的聚合上重用相同的算法。从另一方面说,遍历算法可能需要访问聚合的私有变量。如果这样,将遍历算法放入迭代器中会破坏聚合的封装性。

  3. 迭代器健壮程度如何。在遍历一个聚合的同时更改这个聚合可能是危险的。如果在遍历聚合的时候增加或删除该聚合元素,可能会导致两次访问同一个元素或者遗漏掉某个元素。一个简单的解决办法是拷贝该聚合,并对该拷贝实施遍历,但一般来说这样做代价太高。
    一个健壮的迭代器(robustiterator)保证插入和删除操作不会干扰遍历,且不需拷贝该聚合。当插入或删除元素时,该聚合要么调整迭代器的内部状态,要么在内部的维护额外的信息以保证正确的遍历。

  4. 附加的迭代器操作迭代器的最小接口由First、Next、IsDone和CurrentItem操作组成。其他一些操作可能也很有用。例如,对有序的聚合可用一个Previous操作将迭代器定位到前一个元素。SkipTo操作用于已排序并做了索引的聚合中,它将迭代器定位到符合指定条件的元素对象上。

  5. 使用多态迭代器。它们要求用一个FactoryMethod动态的分配迭代器对象。因此仅当必须多态时才使用它们。否则使用在栈中分配内存的具体的迭代器。多态迭代器有另一个缺点就是客户必须负责删除它们。这容易导致错误,因为容易忘记释放一个使用堆分配的迭代器对象,当一个操作有多个出口时尤其如此。而且其间如果有异常被触发的话,迭代器对象将永远不会被释放。
    注:java的Iterator<T>就是一个典型的多态迭代器,但是使用它时不用担心释放的问题,因为Iterator使用完后会随着生命周期的结束(失去引用)而被垃圾回收机制自动回收(感谢古希腊掌管垃圾回收的神)。

  6. 迭代器是否有特权访问。迭代器可被看为创建它的聚合的一个扩展,迭代器和聚合紧密耦合。迭代器类可包含一些protected操作来访问聚合类的重要的非公共可见的成员。迭代器子类(且只有迭代器子类)可使用这些protected操作来得到对该聚合的特权访问。

  7. 用于复合对象(组合对象)的迭代器。在Composite模式中的那些递归聚合结构上,外部迭代器可能难以实现,因为在该结构中不同对象处于嵌套聚合的多个不同层次,因此一个外部迭代器为跟踪当前的对象必须存储一条纵贯该Composite的路径。有时使用一个内部迭代器会更容易一些。它仅需递归地调用自己即可,这样就隐式地将路径存储在调用栈中,而无需显式地维护当前对象位置。
    如果复合中的节点有一个接口可以从一个节点移到它的兄弟节点、父节点和子节点,那么基于游标的迭代器是个更好的选择。游标只需跟踪当前的节点;它可依赖这种节点接口来遍历该复合对象。复合常常需要用多种方法遍历。前序,后序,中序以及广度优先遍历都是常用的。你可用不同的迭代器类来支持不同的遍历。

  8. 空迭代器。一个空迭代器(NullIterator)是一个退化的迭代器,它有助于处理边界条件。根据定义,一个NullIterator总是已经完成了遍历:即,它的IsDone操作总是返回true。
    空迭代器使得更容易遍历树形结构的聚合(如复合对象)。在遍历过程中的每一节点,都可向当前的元素请求遍历其各个子结点的迭代器。该聚合元素将返回一个具体的迭代器。但叶节点元素返回NullIterator的一个实例。这就使我们可以用一种统一的方式实现在整个结构上的遍历。

应用

问题

看过水浒传的好汉都知道,整个水浒都是围绕着梁山进行的,梁山就像是一个小社会,里面聚集着各色各样的人物,而将这些人物聚集起来的是梁山聚义厅,是替天行道的大旗,在这大旗下,也会有座次排名(当然,小卡拉米没有),这里小编就围绕着座次排名为各位好汉讲解一下迭代模式。首先,想要排名,就需要遍历,所以我们抽象出一个迭代器Iterator,它的具体迭代器是JuyiTingIterator,他们有迭代器四个基本操作first()、next()、isDone()、currentItem(),同时包含一个列表items用于遍历,当前位置position标识遍历时所在位置;聚合用梁山LiangShan表示,具体聚合是聚义厅JuyiTing,方法createIterator()用来创建具体迭代器实例,addItem()用来添加元素。至此,迭代器基本构造完成。

扩展:梁山、聚义厅、迭代器都使用了泛型,这里既可以迭代交椅,也可以迭代其他的如百姓认可度;具体聚合这里是聚义厅,我们还可以定义江湖、朝廷,和他们对应的具体迭代器:江湖的风评迭代器、朝廷的罪大恶极迭代器,当然,此时的实体类就用风评实体类和罪行实体类了。

示例

UML

代码示例

迭代器:迭代器,类似于java的Iterator<T>

package com.ysj.part5.iterator.iterator;

/**
 * 迭代器
 */
public interface Iterator<T> {

    /**
     * 将迭代器重置到集合的第一个元素
     */
    void first();

    /**
     * 将迭代器移动到下一个元素
     * @return
     */
    T next();

    /**
     * 判断迭代器是否到达了集合的末尾,相当于hasNext
     * @return
     */
    Boolean isDone();

    /**
     * 返回当前迭代器指向的元素
     * @return
     */
    T currentItem();

}

具体迭代器:聚义厅迭代器,类似于java的ArrayList的内部类Itr

package com.ysj.part5.iterator.concreteIterator;

import com.ysj.part5.iterator.iterator.Iterator;

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

/**
 * 具体迭代器:聚义厅迭代器
 */
public class JuyiTingIterator<T> implements Iterator<T> {

    /**
     * 元素列表
     */
    private List<T> items = new ArrayList<>();

    /**
     * 当前位置
     */
    private int position = 0;

    public JuyiTingIterator(List<T> items) {
        this.items = items;
        this.position = 0;
    }

    @Override
    public void first() {
        this.position = 0;
    }

    @Override
    public T next() {
        // 判断是否还有下一个元素
        if (isDone()) {
            T item = items.get(position);
            position++;
            return item;
        }
        return null;
    }

    @Override
    public Boolean isDone() {
        if (position < items.size()) {
            return true;
        }
        return false;
    }

    @Override
    public T currentItem() {
        return items.get(position);
    }
}

聚合:梁山,类似于java的List

package com.ysj.part5.iterator.aggregate;

import com.ysj.part5.iterator.iterator.Iterator;

/**
 * 聚合:梁山
 */
public interface LiangShan<T> {

    /**
     * 创建迭代器(发布排名)
     * @return
     */
    Iterator<T> createIterator();

    /**
     * 添加元素(上梁山)
     *
     * @param item
     */
    void addItem(T item);

}

具体聚合:聚义厅,类似于java的ArrayList

package com.ysj.part5.iterator.concreteAggregate;

import com.ysj.part5.iterator.aggregate.LiangShan;
import com.ysj.part5.iterator.concreteIterator.JuyiTingIterator;
import com.ysj.part5.iterator.iterator.Iterator;

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

/**
 * 具体聚合:聚义厅
 */
public class JuyiTing<T> implements LiangShan<T> {

    private List<T> items = new ArrayList<>();

    @Override
    public Iterator<T> createIterator() {
        return new JuyiTingIterator<T>(this.items);
    }

    public void addItem(T item) {
        items.add(item);
    }

}

实体类:交椅

package com.ysj.part5.iterator.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

/**
 * 实体类:交椅
 */
@AllArgsConstructor
@Data
public class Chair {

    /**
     * 交椅编号
     */
    private int no;

    /**
     * 交椅所属人
     */
    private String username;

    /**
     * 交椅所属人诨号
     */
    private String userNick;

}

客户端:客户端

package com.ysj.part5.iterator;

import com.ysj.part5.iterator.aggregate.LiangShan;
import com.ysj.part5.iterator.concreteAggregate.JuyiTing;
import com.ysj.part5.iterator.entity.Chair;
import com.ysj.part5.iterator.iterator.Iterator;

public class Client {

    public static void main(String[] args) {
        // 创建一个聚义厅(聚义厅相当于ArrayList,梁山相当于List)
        LiangShan<Chair> liangshan = new JuyiTing<>();
        liangshan.addItem(new Chair(1, "宋江", "及时雨"));
        liangshan.addItem(new Chair(2, "卢俊义", "玉麒麟"));
        liangshan.addItem(new Chair(3, "吴用", "智多星"));
        liangshan.addItem(new Chair(4, "公孙胜", "入云龙"));
        liangshan.addItem(new Chair(5, "关胜", "大刀"));
        liangshan.addItem(new Chair(6, "林冲", "豹子头"));

        // 创建一个排名榜(相当于Iterator,具体Iterator是聚义厅迭代器)
        System.out.println("====梁山好汉排名====");
        Iterator<Chair> irerator = liangshan.createIterator();
        while (irerator.isDone()) {
            Chair chair = irerator.next();
            System.out.println("第" + chair.getNo() + "把交椅" + chair.getUserNick() + chair.getUsername());
        }
        // 重置迭代器然后再次遍历
        irerator.first();
        System.out.println("====梁山好汉重新排名====");
        while (irerator.isDone()) {
            Chair chair = irerator.currentItem();
            irerator.next();
            System.out.println("第" + chair.getNo() + "把交椅" + chair.getUserNick() + chair.getUsername());
        }

    }

}

已知应用

Java

java中的Iterator就是迭代器模式最经典的应用,几乎所有的集合都提供了iterator() 方法来返回一个迭代器。

Java 8 引入的 Streams API 本质上也是基于迭代器模式的高级抽象。

0

评论区