迭代器模式
<本模式主要作用在于,对于不同数据结构存储的数据(如数组,树,链表,散列表等),我们用相同的方式对其进行迭代遍历,对客服隐藏了底层数据结构的细节,客服只需获得该类迭代器,既可以通过相同的方式进行迭代>
[ 图片来源百度百科 ]
以下我们来看具体使用到迭代器的例子。
我们假设我们系统中有两个类,他们都是用来存东西的,只不过使用的存储方式不同而已。
class Container1{
private Object[] objs;
public Container1(int cap){
this.objs = new Object[cap];
//开始我们先放点东西进去
for(int i = 0 ; i < cap ; i++)
objs[i] = new Integer(i);
}
public Object[] getObjs(){
return objs;
}
}
Container1 这个容器类 是用数组来存放东西的
class Container2{
private ArrayList<Object> objs;
public Container2(){
this.objs = new ArrayList<Object>();
//开始我们先放点东西进去
for(int i = 0 ; i < 5; i++)
objs.putObj(i , new Integer(i));
}
public ArrayList<Object> getObjs(){
return objs;
}
public Object getObj(int index){
if(objs != null)
return objs.get(index);
return null;
}
public void putObj(int index,Object o){
if(objs != null){
objs.set(index,o);
}
}
}
Container2 容器类 是用ArrayList来存放东西的
接下来我们写客服端代码来遍历他们
class Client{
public static void main(String[] args){
//这个容器类使用的数组来放东西的
Container1 con1 = new Container1(10);
//这个容器类使用的ArrayList来放东西的
Container2 con2 = new Container2();
//现在我们来遍历容器类中的元素
for(int i = 0 ; i < con1.getObjs().length ; i++)
System.out.println(con1.getObjs()[i]);
for(int i = 0 ; i < con2.getObjs().size(); i++)
System.out.println(con2.getObj(i));
}
}
可以明显的看到我们遍历这段代码很多地方相似,而且这段代码由于底层数据结构的不同难以修改(因为数组是通过去下标来访问的,而ArrayList 则通过get 方法),这样的代码有一个缺点在于,如果我们新加入一个容器类,假如他底层是用的树的数据结构,我们在客服端遍历他的元素的时候那不又得写一个for循环,这完全违背了我们所说得设计模式中开闭原则(对扩展打开,对修改关闭)。到这里我们要解决得问题非常清晰就是得想办法把这个遍历过程融为一体,即我们可以只通过一种方式就可以对不同数据结构得容器类进行迭代。
很容易想到得一个解决方案那就是通过接口(因为有多态得性质)来规范化迭代行为。我们先定义一个迭代器接口如下
//该接口规定了两个方法
interface Iterator{
//判断容器是否还有下一个元素
boolean hasNext();
//获取容器下一个元素
Object next();
}
我们直接用容器类来实现该接口?不不不!我们现在还不想改变容器类的代码(以后可能会改)。在这里我们将另外写一个该接口实现类,应该是两个分别是用来为迭代这两个容器的元素。
//数组方式的容器迭代
class Container1Iterator implements Iterator{
Container1 container1;
private int cur = 0;
public Container1Iterator(Container1 container1){
this.container1 = container1;
}
//判断容器是否还有下一个元素
public boolean hasNext(){
if(container1.getObjects().length <= cur)
return false;
return true;
}
//获取容器下一个元素
public Object next(){
return container1.getObjects()[cur];
}
}
//以下实现与上面类似此处不累赘
class Container2Iterator implements Iterator{
//.......
}
好既然有了迭代器实现类我们再来看看客服端代码怎么修改
class Client{
private void traverse(Iterator iterator){
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
public static void main(String[] args){
//这个容器类使用的数组来放东西的
Container1 con1 = new Container1(10);
//这个容器类使用的ArrayList来放东西的
Container2 con2 = new Container2();
traverse(new Container1Iterator(con1));
traverse(new Container2Iterator(con2));
}
}
完美我们解决了代码重复的问题,而且当有新的容器类加入时,只要他有Iterator的实现类我们仍然可以通过该方式来进行迭代。到这里可能有的人还不太满意,因为客服端代码与容器具体类(Container1Iterator , Container1Iterator )是强耦合的,这不是我们喜欢的。
我们来对上面的类来改进以下,我们将两个容器"融为一体",通过接口多态的性质。
我们先定义一个公共的接口,该接口规定了一个迭代器创建方法。
interface Iterable{
//该方法用于创建一个迭代器类
Iterator iterator();
}
然后让容器类去实现他。
class Container1 implements Iterable{
public Iterator iterator(){
return new Container1Iterator(this);
}
//省略其他部分......
}
class Container2 implements Iterable{
public Iterator iterator(){
return new Container2Iterator(this);
}
//省略其他部分......
}
好我们两个容器类都实现同一个接口,我们接口多态性质终于可以大展身手了。
class Client{
private void traverse(Iterable Iterable){
Iterator iterator = Iterable.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
到这里我们迭代器模式已经完成。
总结迭代器模式,让客服端无需关心底层存储的数据的方式,并且可以通过相同的方式对不同的容器进行迭代。