[把你的理性思维慢慢变成条件反射]
本文,我们讲介绍迭代器模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:
操作系统:win7 x64
其他软件:eclipse mars,jdk8
-------------------------------------------------------------------------------------------------------------------------------------
经典问题:
某一个特定集合(聚合)的遍历(迭代)
思路分析:
要点一:所谓遍历,就是要无一例外的全部访问。
要点二:遍历(迭代)只讲全面,而忽略顺序。即,任何位置的元素都可以作为起点。
示例工程:
迭代器模式模板代码:
创建Aggregate.java文件,具体内容如下:
package com.csdn.ingo.gof_Iterator;
import java.util.ArrayList;
import java.util.List;
public abstract class Aggregate {
protected List<Object> objects = new ArrayList<Object>();
public Aggregate(List objects){
this.objects = objects;
}
public void add(Object obj){
this.objects.add(obj);
}
public void remove(Object obj){
this.objects.remove(obj);
}
public List getObjects(){
return this.objects;
}
public abstract Iterator createIterator();
}
创建ConcreteAggregate.java文件,具体内容如下:
package com.csdn.ingo.gof_Iterator;
import java.util.ArrayList;
import java.util.List;
public class ConcreteAggregate extends Aggregate {
public ConcreteAggregate(List objects) {
super(objects);
// TODO Auto-generated constructor stub
}
private List<Object> items = new ArrayList<Object>();
public int Count;
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
public int getCount() {
return items.size();
}
public void setCount(int count) {
Count = count;
}
}
创建ConcreteIterator.java文件,具体内容如下:
package com.csdn.ingo.gof_Iterator;
import java.util.List;
public class ConcreteIterator extends Iterator {
private ConcreteAggregate aggregate;
private List aggregates;
private int cursor1;
private int cursor2;
public ConcreteIterator (ConcreteAggregate aggregate){
this.aggregate = aggregate;
this.aggregates = aggregate.getObjects();
cursor1 = 0;
cursor2 = aggregates.size()-1;
}
@Override
public boolean isFirst() {
return (cursor2==-1);
}
@Override
public void previous() {
if(cursor2>-1){
cursor2--;
}
}
@Override
public boolean isLast() {
return (cursor1==aggregates.size());
}
@Override
public Object getCurrentItem() {
return aggregates.get(cursor1);
}
@Override
public Object getPreviousItem() {
return aggregates.get(cursor2);
}
@Override
public void next() {
if(cursor1<aggregates.size()){
cursor1++;
}
}
}
创建Iterator.java文件,具体内容如下:
package com.csdn.ingo.gof_Iterator;
public abstract class Iterator
{
public abstract boolean isFirst();
public abstract void next();
public abstract void previous();
public abstract boolean isLast();
public abstract Object getCurrentItem();
public abstract Object getPreviousItem();
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Iterator;
import java.util.ArrayList;
import java.util.List;
public class Window {
public static void main(String[] args) {
List products =new ArrayList();
products.add("AAA");
products.add("BBB");
products.add("CCC");
products.add("DDD");
products.add("EEE");
Iterator iterator;
Aggregate list;
list = new ConcreteAggregate(products);
iterator = list.createIterator();
System.out.println("---正向遍历---");
while(!iterator.isLast()){
System.out.println(iterator.getCurrentItem()+",");
iterator.next();
}
System.out.println("------------");
System.out.println("---反向遍历---");
while(!iterator.isFirst()){
System.out.println(iterator.getPreviousItem()+",");
iterator.previous();
}
}
}
【上面的代码直接看的话,可能有难度。先把代码提供给各位看官是希望现将代码运行起来,观察结果。下面我们介绍相关的概念与原理】
模式总结:
迭代器模式结构图:
迭代器模式:
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
组成部分:
Iterator(抽象迭代器):定义了访问和遍历元素的接口,声明了用于遍历元素的方法。
ConcreteIterator(具体迭代器):实现抽象迭代器,完成对聚合对象的遍历,并且通过游标记录当前遍历的索引位置。
Aggregate(抽象聚合类):用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象。
ConcreteAggregate(具体聚合类):实现了抽象聚合类,返回一个具体的迭代器实例。
特别提醒:迭代器模式应用到了工厂方法模式的设计方法。【详情参考:http://blog.youkuaiyun.com/abcd898989/article/details/52859560】
JDK的内置迭代器:【以下内容来自其他博文与JDK源码】
在java中,常见的list与set都继承或者实现了java.util.Collection接口。
Collection的具体内容如下:
我们看到除了add,remove方法外,还提供了iterator方法,用于返回一个迭代器对象。具体的java聚合类可以通过实现该iterator方法返回一个具体的迭代器对象。
JDK中定义的抽象迭代器接口内容如下:
以上方法的官方解释如下:
测试代码:
package com.csdn.ingo.gof_Iterator;
import java.util.ArrayList;
import java.util.List;
public class Window {
public static void main(String[] args) {
List products =new ArrayList();
products.add("AAA");
products.add("BBB");
products.add("CCC");
products.add("DDD");
products.add("EEE");
java.util.Iterator iterator = products.iterator();
iterator.next();
iterator.remove();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
特别提醒:remove()方法本身没有移动游标的位置。因此,在当前位置调用了remove()方法后,当前位置等于是“空”元素。如果不移动游标位置,再次调用remove()方法,将会抛出如下异常:
JDK的List与迭代器的实现说明,如下图:【以下内容自于其他博文,详情参考文章末尾】
在JDK中,实际情况比上图要复杂很多,在上图中,List接口除了继承Collection接口的iterator()方法外,还增加了新的工厂方法listIterator(),专门用于创建ListIterator类型的迭代器。
在List的子类LinkedList中实现了该方法,可用于创建具体的ListIterator子类ListItr的对象,如下所示:
listIterator()方法用于返回具体迭代器ListItr类型的对象。在JDK源码中,AbstractList中的iterator()方法调用了listIterator()方法,如下:
客户端通过调用LinkedList类的iterator()方法,即可得到一个专门用于遍历Linkedlist的迭代器对象。
问题:
既然有了iterator()方法,为什么还要提供一个listIterator()方法呢?这两个方法的功能不会存在重复吗?
解释:
由于在Iterator接口中定义的方法太少,只有三个,通过这三个方法只能实现正向遍历,而有时候我们需要对一个聚合对象进行逆向遍历等操作,因此在JDK的ListIterator接口中声明了用于逆向遍历的hasPrevious()和previous()等方法,如果客户端需要调用这两个方法来实现逆向遍历,就不能再使用iterator()方法来创建迭代器了,因为此时创建的迭代器对象是不具有这两个方法的。我们只能通过如下代码来创建ListIterator类型的迭代器对象:ListIterator i = c.listIterator();正因为如此,在JDK的List接口中不得不增加对listIterator()方法的声明,该方法可以返回一个ListIterator类型的迭代器,ListIterator迭代器具有更加强大的功能。
反思:
应用场景:
- 需要(多种方式)遍历某个聚合对象,并且要求隐藏遍历细节。
- 对遍历对象需要进行,访问与控制分离。(可以理解为读写分离)
- 对客户端访问时提供统一的访问接口,聚合对象的结构差异不会影响客户端调用。
优点:
- 遍历的方法可定义多个,进而由不同的迭代器实现类完成即可。客户端保持统一调用方式。
- 对聚合对象的访问与控制进行了职责分离,简化了聚合对象的设计。
- 客户端面向接口编程,符合“开闭原则”。
缺点:
- 迭代器模式的实现过程需要大量的interface,class支撑。增删某个聚合类型都需要新增一组迭代器实现。其实现过程可能较为复杂。
- 完整的迭代功能较为复杂,在设计时,需要充分考虑未来的需求场景,错误的设计实现过程,很可能对性能,安全产生不利影响。
-------------------------------------------------------------------------------------------------------------------------------------
至此,被说了很多遍的设计模式---迭代器模式 结束
特别备注:上文中关于JDK源码的描述与List结构图,可能与当前JDK8版本有分歧。建议各位看官最好能够Debug跟踪学习。
参考资料:
图书:《大话设计模式》
其他博文:http://blog.csdn.NET/lovelion/article/details/7563445