名称
迭代器模式(ITERATOR),别名:游标(Cursor)
目的
提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。
适用性
迭代器模式可用来:
访问一个聚合对象的内容而无需暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口 (即, 支持多态迭代)。
结构
迭代器(Iterator):迭代器定义访问和遍历元素的接口。
具体迭代器(ConcreteIterator):
具体迭代器实现迭代器接口。
对该聚合遍历时跟踪当前位置。
聚合(Aggregate):聚合定义创建相应迭代器对象的接口。
具体聚合(ConcreteAggregate):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
协作
ConcreteIterator跟踪聚合中的当前对象,并能够计算出待遍历的后继对象。
效果
优点
支持以不同的方式(比如正向、反向或者特定条件下的遍历)遍历一个聚合。仅需用一个不同的迭代器的实例代替原先的实例即可。
迭代器简化了聚合的接口。有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了,这样就简化了聚合的接口。
在同一个聚合上可以有多个遍历。每个迭代器保持它自己的遍历状态,可以同时进行多个遍历。
封装遍历逻辑。迭代器模式封装了遍历逻辑,使得客户端代码不需要关心聚合对象的内部结构。
统一的访问接口。为遍历聚合对象提供了一个统一的接口,使得客户端能够一致地访问不同的聚合对象。
职责分离。将遍历逻辑从聚合对象本身分离出来,使得聚合对象专注于自身的职责,即存储和管理对象。
易于扩展。当需要添加新的遍历方式时,可以通过添加新的迭代器类来实现,而不需要修改现有的聚合类。
缺点
增加系统的复杂性。为了支持迭代器模式,需要引入额外的类(迭代器类),这会增加系统的复杂性。
潜在的并发问题。如果迭代器与聚合对象之间的状态不一致,可能会导致并发访问的问题。
额外的开发工作量。每当新增加一个聚合类时,可能都需要对应地增加一个新的迭代器类,增加了开发工作量。
资源消耗。迭代器的创建和维护可能会带来一定的资源开销,尤其是在处理大量数据的情况下。
实现
谁控制该迭代。一个基本的问题是决定由哪一方来控制该迭代,是迭代器还是使用该迭代器的客户。当由客户来控制迭代时,该迭代器称为一个外部迭代器(externaliterator),而当由迭代器控制迭代时,该迭代器称为一个内部迭代器(internaliterator)。使用外部迭代器的客户必须主动推进遍历的步伐,显式地向迭代器请求下一个元素。相反地,若使用内部迭代器,客户只需向其提交一个待执行的操作,而迭代器将对聚合中的每一个元素实施该操作。
http://bantang.xyz/archives/wai-bu-die-dai-qi-he-nei-bu-die-dai-qi 谁定义遍历算法。迭代器不是唯一可定义遍历算法的地方。聚合本身也可以定义遍历算法,并在遍历过程中用迭代器来存储当前迭代的状态。我们称这种迭代器为一个游标(cursor),因为它仅用来指示当前位置。客户会以这个游标为一个参数调用该聚合的Next操作,而Next操作将改变这个指示器的状态。
如果迭代器负责遍历算法,那么将易于在相同的聚合上使用不同的迭代算法,同时也易于在不同的聚合上重用相同的算法。从另一方面说,遍历算法可能需要访问聚合的私有变量。如果这样,将遍历算法放入迭代器中会破坏聚合的封装性。迭代器健壮程度如何。在遍历一个聚合的同时更改这个聚合可能是危险的。如果在遍历聚合的时候增加或删除该聚合元素,可能会导致两次访问同一个元素或者遗漏掉某个元素。一个简单的解决办法是拷贝该聚合,并对该拷贝实施遍历,但一般来说这样做代价太高。
一个健壮的迭代器(robustiterator)保证插入和删除操作不会干扰遍历,且不需拷贝该聚合。当插入或删除元素时,该聚合要么调整迭代器的内部状态,要么在内部的维护额外的信息以保证正确的遍历。附加的迭代器操作迭代器的最小接口由First、Next、IsDone和CurrentItem操作组成。其他一些操作可能也很有用。例如,对有序的聚合可用一个Previous操作将迭代器定位到前一个元素。SkipTo操作用于已排序并做了索引的聚合中,它将迭代器定位到符合指定条件的元素对象上。
使用多态迭代器。它们要求用一个FactoryMethod动态的分配迭代器对象。因此仅当必须多态时才使用它们。否则使用在栈中分配内存的具体的迭代器。多态迭代器有另一个缺点就是客户必须负责删除它们。这容易导致错误,因为容易忘记释放一个使用堆分配的迭代器对象,当一个操作有多个出口时尤其如此。而且其间如果有异常被触发的话,迭代器对象将永远不会被释放。
注:java的Iterator<T>就是一个典型的多态迭代器,但是使用它时不用担心释放的问题,因为Iterator使用完后会随着生命周期的结束(失去引用)而被垃圾回收机制自动回收(感谢古希腊掌管垃圾回收的神)。
迭代器是否有特权访问。迭代器可被看为创建它的聚合的一个扩展,迭代器和聚合紧密耦合。迭代器类可包含一些protected操作来访问聚合类的重要的非公共可见的成员。迭代器子类(且只有迭代器子类)可使用这些protected操作来得到对该聚合的特权访问。
用于复合对象(组合对象)的迭代器。在Composite模式中的那些递归聚合结构上,外部迭代器可能难以实现,因为在该结构中不同对象处于嵌套聚合的多个不同层次,因此一个外部迭代器为跟踪当前的对象必须存储一条纵贯该Composite的路径。有时使用一个内部迭代器会更容易一些。它仅需递归地调用自己即可,这样就隐式地将路径存储在调用栈中,而无需显式地维护当前对象位置。
如果复合中的节点有一个接口可以从一个节点移到它的兄弟节点、父节点和子节点,那么基于游标的迭代器是个更好的选择。游标只需跟踪当前的节点;它可依赖这种节点接口来遍历该复合对象。复合常常需要用多种方法遍历。前序,后序,中序以及广度优先遍历都是常用的。你可用不同的迭代器类来支持不同的遍历。空迭代器。一个空迭代器(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 本质上也是基于迭代器模式的高级抽象。
评论区