一、定义
GOF上对迭代器模式的意图这样描述:提供一种方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。
二、场景设计
一个聚合对象,如List,应该提供方法让人来访问他的元素,又不需要暴露它的内部结构。同时,可能会有以不同方式遍历列表的需求。为了不让List接口中充斥着各种不同的遍历操作,迭代器模式可以排得上用场。迭代器模式的关键思想:将对列表的访问和遍历从列表对象中分离,并放入一个迭代器对象中。迭代器类定义了一个访问该列表元素的接口。实例化一个迭代器对象之前,必须提供待遍历的列表。
将遍历机制和列表对象分离开来可以使得我们更灵活地重用遍历机制,同时可以定义不同的迭代器来实现不同的遍历策略。举个例子,针对树状组合结构,可以提供先根,中根等一系列遍历方式。
注意,由于迭代器和列表是耦合在一起的,使用普通的迭代器时,客户对象必须知道要遍历的是列表而不是其他聚合结构,可以使用多态迭代来做到不需要改变客户代码就可以改变该聚合类。
还是列表的例子,定义一个抽象列表类AbstractList,它提供操作列表的公共接口。类似的,需要一个抽象的迭代器类Iterator。它定义公共的迭代接口。然后我们可以为每个不同的列表实现具体的Iterator子类。类图如下:
既然要让列表对象创建相应的迭代器对象,列表对象需要提供CreateIterator这样的操作,由列表对象自己创建一个适用于自己类型的迭代器对象。
这种创建方式是工厂模式 的例子,这里用它使得一个客户可向一个列表对象请求合适的迭代器。工厂模式产生了两个类层次,一个是列表的,一个是迭代器的。而CreateIterator 联系了这两个类层次。
Java容器类的迭代器就使用了多态迭代的架构。
三、类图
四、代码示例
迭代器模式可以应用到组合模式上,因此我在原来看组合模式的代码基础上增加了迭代的访问的功能。
INode.java
package inodes;
import iterators.INodeIterator;
import java.util.*;
public interface INode {
int getFileNum();
void Add(INode node);
void Remove(INode node);
void OutputInfo();
INodeIterator createIterator(HashSet<INode> set);
INodeIterator iterator();
}
INodeFile.java
package inodes;
import java.util.HashSet;
import iterators.FileIterator;
import iterators.INodeIterator;
public class INodeFile implements INode {
String name;
public INodeFile(String name)
{
this.name=name;
}
@Override
public int getFileNum() {
return 1;
}
@Override
public void Add(INode node) {
System.out.println("File-----No subdirectory");
}
@Override
public void Remove(INode node) {
System.out.println("File-----No subdirectory");
}
@Override
public void OutputInfo() {
System.out.print("file: "+name+"|");
}
public String toString()
{
return "File: "+this.name+" ";
}
@Override
public INodeIterator createIterator(HashSet<INode> set) {
return new FileIterator(this,set);
}
@Override
public INodeIterator iterator() {
return createIterator(new HashSet<INode>());
}
}
INodeDirectory.java
package inodes;
import iterators.DirectoryIterator;
import iterators.INodeIterator;
import java.util.*;
public class INodeDirectory implements INode {
private LinkedList<INode> list;
String name;
public INodeDirectory(String name)
{
this.name=name;
list=new LinkedList<INode>();
}
@Override
public int getFileNum() {
int sum=0;
for(INode temp:list)
{
sum+=temp.getFileNum();
}
return sum;
}
@Override
public void Add(INode node) {
list.add(node);
}
@Override
public void Remove(INode node) {
list.remove(node);
}
@Override
public void OutputInfo() {
System.out.print("directory: "+name+"|");
int i=0;
for(INode temp:list)
{
temp.OutputInfo();
}
System.out.println("");
}
public String toString()
{
return "Dir: "+this.name+" ";
}
@Override
public INodeIterator createIterator(HashSet<INode> set) {
return new DirectoryIterator(this,this.list,set);
}
@Override
public INodeIterator iterator() {
return createIterator(new HashSet<INode>());
}
}
以上是INode类层次结构,相当于类图中的Aggregate层次。可以看到,每个INode子类的createIterator方法都创建了一个特定类型的Iterator来处理自己的遍历。
INodeIterator.java
package iterators;
import inodes.INode;
import java.util.*;
public abstract class INodeIterator {
protected INode node;
protected HashSet<INode> visited=null;
public INodeIterator(INode node,HashSet<INode> vis)
{
this.node=node;
this.visited=vis;
}
public abstract int depth();
public abstract boolean hasNext();
public abstract INode next();
}
FileIteraor.java
package iterators;
import java.util.HashSet;
import inodes.INode;
public class FileIterator extends INodeIterator {
public FileIterator(INode node, HashSet<INode> vis) {
super(node, vis);
}
@Override
public int depth() {
return 0;
}
@Override
public boolean hasNext() {
return !visited.contains(node);
}
@Override
public INode next() {
if(visited.contains(node))
return null;
visited.add(node);
return node;
}
}
DirectoryIterator.java
package iterators;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import inodes.INode;
public class DirectoryIterator extends INodeIterator {
protected INodeIterator subiterator = null;
protected INode peek = null;
protected Iterator<INode> children = null;
public DirectoryIterator(INode node, LinkedList<INode> components,HashSet<INode> vis) {
super(node, vis);
children=components.iterator();
}
@Override
public int depth() {
if(subiterator != null)
return subiterator.depth()+1;
return 0;
}
@Override
public boolean hasNext() {
if(peek == null)
{
peek = next();
}
return peek != null;
}
@Override
public INode next() {
if(peek != null)
{
INode o = peek;
peek = null;
return o;
}
if(!visited.contains(node))
{
visited.add(node);
return node;
}
return nextDescendant();
}
protected INode nextDescendant()
{
while(true){
if(subiterator != null)
{
if(subiterator.hasNext())
{
return subiterator.next();
}
}
if(!children.hasNext())
{
return null;
}
INode i=children.next();
if(!visited.contains(i))
{
subiterator=i.createIterator(visited);
}
}
}
}
以上是INodeIterator类层次结构,对应于类图中的Iterator类层次结构,定义用于遍历的接口。
Client.java
package client;
import inodes.INode;
import inodes.INodeDirectory;
import inodes.INodeFile;
import iterators.INodeIterator;
public class Client {
public static void main(String[] args) {
INode plant=new INodeDirectory("plant");
INode root=new INodeDirectory("root");
INode music=new INodeFile("music");
INode pdf=new INodeFile("pdf");
INode movie=new INodeFile("movie");
plant.Add(music);
plant.Add(pdf);
plant.Add(movie);
INode book=new INodeFile("book");
root.Add(plant);
root.Add(book);
System.out.println(root.getFileNum());
//root.OutputInfo();
INodeIterator i=root.iterator();
while(i.hasNext())
{
for(int j=0;j<i.depth()*4;j++)
System.out.print(" ");
System.out.println(i.next());
}
}
}
程序运行结果:
从Client.java中可以看出,客户代码从根节点获得迭代器后,可以很便捷地遍历打印出建立的文件树结构,而不需要知道INode,INodeFile,INodeDirectory的具体组合方式。