一、什么是迭代子模式?
迭代子(Iterator)模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴漏聚集的内部表象。
二、迭代子模式的结构
迭代子模式有两种实现方式,分别是白箱聚集与外禀迭代子和黑箱聚集与内禀迭代子。
(1)白箱聚集与外禀迭代子
如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口。
如果聚集对象为所有对象提供同一个接口,也就是宽接口的话,当然会满足迭代子模式对迭代子对象的要求。但是,这样会破坏对聚集对象的封装。这种提供宽接口的聚集叫做白箱聚集。
由于聚集自己实现迭代逻辑,并向外部提供适当的接口,使得迭代子可以从外部控制聚集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫做游标迭代子(Cursor Iterator)。由于迭代子是在聚集结构之外的,因此这样的迭代子又叫做外禀迭代子(Extrinsic Iterator)。
一个典型的由白箱聚集与外禀迭代子组成的系统如下图所示,在这个实现中具体迭代子角色是一个外部类,而具体聚集角色向外界提供遍历聚集元素的接口。
迭代子模式涉及到以下几个角色:
●抽象迭代子(Iterator)角色:此抽象角色定义出遍历元素所需的接口。
●具体迭代子(ConcreteIterator)角色:此角色实现了Iterator接口,并保持迭代过程中的游标位置。
●聚集(Aggregate)角色:此抽象角色给出创建迭代子(Iterator)对象的接口。
●具体聚集(ConcreteAggregate)角色:实现了创建迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。
●客户端(Client)角色:持有对聚集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。
三、代码类
抽象聚集角色类,这个角色规定出所有的具体聚集必须实现的接口。迭代子模式要求聚集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。
public abstract class Aggregate { /** * 工厂方法,创建相应迭代子对象的接口 */ public abstract Iterator createIterator(); }
具体聚集角色类,实现了抽象聚集角色类所要求的接口,也就是createIterator()方法。此外,还有方法getElement()向外界提供聚集元素,而方法size()向外界提供聚集的大小等。
public class ConcreteAggregate extends Aggregate { private Object[] objArray = null; /** * 构造方法,传入聚合对象的具体内容 */ public ConcreteAggregate(Object[] objArray){ this.objArray = objArray; } @Override public Iterator createIterator() { return new ConcreteIterator(this); } /** * 取值方法:向外界提供聚集元素 */ public Object getElement(int index){ if(index < objArray.length){ return objArray[index]; }else{ return null; } } /** * 取值方法:向外界提供聚集的大小 */ public int size(){ return objArray.length; } }
抽象迭代子角色类
public interface Iterator { /** * 迭代方法:移动到第一个元素 */ public void first(); /** * 迭代方法:移动到下一个元素 */ public void next(); /** * 迭代方法:是否为最后一个元素 */ public boolean isDone(); /** * 迭代方法:返还当前元素 */ public Object currentItem(); }
具体迭代子角色类
public class ConcreteIterator implements Iterator { //持有被迭代的具体的聚合对象 private ConcreteAggregate agg; //内部索引,记录当前迭代到的索引位置 private int index = 0; //记录当前聚集对象的大小 private int size = 0; public ConcreteIterator(ConcreteAggregate agg){ this.agg = agg; this.size = agg.size(); index = 0; } /** * 迭代方法:返还当前元素 */ @Override public Object currentItem() { return agg.getElement(index); } /** * 迭代方法:移动到第一个元素 */ @Override public void first() { index = 0; } /** * 迭代方法:是否为最后一个元素 */ @Override public boolean isDone() { return (index >= size); } /** * 迭代方法:移动到下一个元素 */ @Override public void next() { if(index < size) { index ++; } } }
客户端类
public class Client { public void operation(){ Object[] objArray = {"One","Two","Three","Four","Five","Six"}; //创建聚合对象 Aggregate agg = new ConcreteAggregate(objArray); //循环输出聚合对象中的值 Iterator it = agg.createIterator(); while(!it.isDone()){ System.out.println(it.currentItem()); it.next(); } } public static void main(String[] args) { Client client = new Client(); client.operation(); } }
(2)黑箱聚集与内禀迭代子
如果一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是所谓的窄接口。
聚集对象为迭代子对象提供一个宽接口,而为其他对象提供一个窄接口。换言之,聚集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象能够对聚集对象有足够的了解,从而可以进行迭代操作。但是,聚集对象应当避免向其他的对象提供这些方法,因为其他对象应当经过迭代子对象进行这些工作,而不是直接操控聚集对象。
在JAVA语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类。这样迭代子对象将可以像聚集对象的内部成员一样访问聚集对象的内部结构。下面给出一个示意性的实现,说明这种双重接口的结构时怎么样产生的,以及使用了双重接口结构之后迭代子模式的实现方案。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做黑箱实现方案。
由于迭代子是聚集的内部类,迭代子可以自由访问聚集的元素,所以迭代子可以自行实现迭代功能并控制对聚集元素的迭代逻辑。由于迭代子是在聚集的结构之内定义的,因此这样的迭代子又叫做内禀迭代子(Intrinsic Iterator)。
为了说明黑箱方案的细节,这里给出一个示意性的黑箱实现。在这个实现里,聚集类ConcreteAggregate含有一个内部成员类ConcreteIterator,也就是实现了抽象迭代子接口的具体迭代子类,同时聚集并不向外界提供访问自己内部元素的方法。
代码类:
抽象聚集角色类,这个角色规定出所有的具体聚集必须实现的接口。迭代子模式要求聚集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。
public abstract class Aggregate { /** * 工厂方法,创建相应迭代子对象的接口 */ public abstract Iterator createIterator(); }
抽象迭代子角色类
public interface Iterator { /** * 迭代方法:移动到第一个元素 */ public void first(); /** * 迭代方法:移动到下一个元素 */ public void next(); /** * 迭代方法:是否为最后一个元素 */ public boolean isDone(); /** * 迭代方法:返还当前元素 */ public Object currentItem(); }
具体聚集角色类,实现了抽象聚集角色所要求的接口,也就是createIterator()方法。此外,聚集类有一个内部成员类ConcreteIterator,这个内部类实现了抽象迭代子角色所规定的接口;而工厂方法createIterator()所返还的就是这个内部成员类的实例。
public class ConcreteAggregate extends Aggregate { private Object[] objArray = null; /** * 构造方法,传入聚合对象的具体内容 */ public ConcreteAggregate(Object[] objArray){ this.objArray = objArray; } @Override public Iterator createIterator() { return new ConcreteIterator(); } /** * 内部成员类,具体迭代子类 */ private class ConcreteIterator implements Iterator { //内部索引,记录当前迭代到的索引位置 private int index = 0; //记录当前聚集对象的大小 private int size = 0; /** * 构造函数 */ public ConcreteIterator(){ this.size = objArray.length; index = 0; } /** * 迭代方法:返还当前元素 */ @Override public Object currentItem() { return objArray[index]; } /** * 迭代方法:移动到第一个元素 */ @Override public void first() { index = 0; } /** * 迭代方法:是否为最后一个元素 */ @Override public boolean isDone() { return (index >= size); } /** * 迭代方法:移动到下一个元素 */ @Override public void next() { if(index < size) { index ++; } } } }
客户端类
public class Client { public void operation(){ Object[] objArray = {"One","Two","Three","Four","Five","Six"}; //创建聚合对象 Aggregate agg = new ConcreteAggregate(objArray); //循环输出聚合对象中的值 Iterator it = agg.createIterator(); while(!it.isDone()){ System.out.println(it.currentItem()); it.next(); } } public static void main(String[] args) { Client client = new Client(); client.operation(); } }
四、迭代子模式的优点
(1)迭代子模式简化了聚集的接口。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。
(2)每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。
(3)由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。