1.迭代器模式
迭代器(子)模式(Iterator)又叫游标模式,是对象的行为模式。迭代器模式可以顺序的访问一个聚集中的元素,而不用暴露聚集的内部表象。
2.为什么需要迭代器模式
一个聚集持有多个对象,就需要对客户端提供遍历对象的方法,使用过程可能会出现以下问题:
(1)迭代逻辑没有改变,但是需要将一种聚集换成另一种聚集。
(2)聚集没有改变,但是迭代方式需要改变,例如新增可以删除元素的功能。
出现这些问题,就需要修改客户端代码或者修改聚集的方法。这是因为聚集对象和迭代逻辑耦合过紧,需要新增迭代子对象,将迭代逻辑封装到里面,从而与聚集本身分开。
不同的聚集可以提供相同的迭代器对象,从而使客户端无需知道知道聚集的底层结构。
一个聚集可以提供多个不同的迭代对象,从而使得遍历逻辑的变化不会影响到聚集本身。
3.迭代器模式结构
迭代器模式的一般结构图如下:
- Iterator:抽象迭代器,定义遍历元素所需的所有接口
- ConcreteIterator:具体迭代器,实现Iterator的所有接口,并保持迭代过程中的游标位置。
- Aggregate:聚集角色,提供聚集接口和创建迭代器的接口。
- ConcreteAggregate:具体聚集角色,持有元素对象,并实现创建具体迭代器对象的接口。
- Client:客户端,持有聚集及其迭代器对象的引用,使用迭代器对象访问或者操作聚集中的元素。
4.迭代器的两种实现方式
(1)白箱聚集和外禀迭代子
聚集对外界透明,提供访问自己元素的接口,迭代子只保持一个迭代的游标位置,并通过聚集的接口访问其中的元素。
示例代码:
Aggregate.java(抽象聚集)
package com.patterns.iterator.whitebox;
abstract public class Aggregate
{
public Iterator createIterator()
{
return null;
}
}
package com.patterns.iterator.whitebox;
public class ConcreteAggregate extends Aggregate
{
/**
* 为简单起见,初始化一个数组
*/
private Object objs[]= {"Monk Tang",
"Monkey", "Pigsy",
"Sandy", "Horse"};
/**
* 实现创建迭代器的方法
*/
public Iterator createIterator()
{
return new ConcreteIterator(this);
}
/**
* 对外透明访问持有元素的方法,迭代子通过次方法遍历
* @param index
* @return
*/
public Object getElement(int index)
{
if (index < objs.length)
{
return objs[index];
}
else
{
return null;
}
}
public int size()
{
return objs.length;
}
}
public interface Iterator
{
void first();
void next();
boolean isDone();
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;
size = agg.size();
index = 0 ;
}
public void first()
{
index = 0 ;
}
public void next()
{
if (index < size)
{
index++;
}
}
public boolean isDone()
{
return (index >= size);
}
public Object currentItem()
{
return agg.getElement(index);
}
}
Client.java(客户端)
public class Client
{
//迭代器对象
private Iterator it;
//聚集对象
private Aggregate agg = new ConcreteAggregate();
public void operation()
{
it = agg.createIterator();
while( !it.isDone() )
{
System.out.println(it.currentItem().toString());
it.next();
}
}
public static void main(String[] args)
{
Client client = new Client();
client.operation();
}
}
聚集提供了遍历放,但是迭代器将迭代过程抽象化,使得客户端和迭代逻辑分开。在聚集发生变化时,避免修改客户端;在迭代方法发生变化时,避免修改聚集本身。
如果系统需要对不同聚集进行迭代。为每个聚集实现一个具体迭代器,可以让系统使用统一的迭代接口进行遍历。
(2)黑箱聚集与内禀迭代器
黑箱聚集不向外部提供遍历自己元素对象的接口,为了是迭代器访问聚集的元素,需要把迭代器实现成聚集的内部类(内禀迭代器)。
这种迭代器的实现和(1)白箱聚集和外禀迭代子 的实现不同在于聚集的实现和具体迭代器的实现,其他代码与(1)中一样
ConcreteAggregate.java
package com.patterns.iterator.blackbox;
/**
* 不向外提供访问内部元素的方法
* @author chenxh
*
*/
public class ConcreteAggregate extends Aggregate
{
/**
* 内部元素
*/
private Object[] objs = {"Monk Tang",
"Monkey", "Pigsy",
"Sandy", "Horse"};
/**
* 创建迭代器
*/
public Iterator createIterator()
{
return new ConcreteIterator();
}
/**
* 聚集内部类实现的迭代器,可以访问外部类的私有属性和方法
* @author chenxh
*
*/
private class ConcreteIterator
implements Iterator
{
//游标
private int currentIndex = 0;
public void first()
{
currentIndex = 0;
}
public void next()
{
if ( currentIndex < objs.length )
{
currentIndex++;
}
}
public boolean isDone()
{
return (currentIndex == objs.length);
}
public Object currentItem()
{
return objs[currentIndex];
}
}
}
5.迭代器模式的优缺点
(1)优点
- 将迭代过程封装到迭代器中,简化了聚集代码,并且简化了客户端的遍历代码。
- 每个聚集可以有几个不同的迭代器,互不影响。
- 封装性良好,客户端不必知道聚集的遍历算法,只要得到迭代器就可以使用
(2)缺点
- 迭代器容易让人产生聚集都是线性结构的错觉,有可能会得到意料之外的结果
- 对于简单聚集,例如ArrayList,迭代器比较繁琐。