3.对组合结构进行迭代:
很容易设计算法来遍历组合结构,访问其中每个成员,并执行某种操作.创建迭代器也许比创建递归算法更加困难.难点在于如何返回对其他程序的控制权、记录某种标签,以便于迭代器能够返回继续执行。组合结构提供一个很好的迭代器范例,开发起来有一定难度。
有人可能会认为在建模时每个问题域的组合结构都需要一个新的迭代器类。实际上,我们可以设计一个可复用的组合迭代器;在需要时,稍微调整组合类就可以获取自己所需的迭代器。
设计要求组合迭代器像组合结构本身一样也是可递归的。为迭代处理某组合结构,应该迭代处理其子类结构--也许这听起来有点复杂。首先需要决定是返回前序节点,还是后续节点。如果选择先序遍历,必须首先遍历子节点,注意每个子节点可能就是一个组合对象。需注意的细节是我们必须维护两个迭代器:第一个迭代器(图1中标识为1)记录哪个节点是当前节点。这个迭代器是个简单的列表迭代器,对子对象列表进行迭代处理。第二个迭代器(图1中的2)把当前子节点当作组合结构进行迭代处理。图1给出决定组合迭代中当前节点的三个方向:
迭代组合结构需要注意的三个地方:0表示头节点,1表示顺序迭代子节点,
2表示把每个子节点当作组合结构进行迭代处理
为确定组合结构迭代器的设计思路,迭代叶节点难度也许不大,但迭代有子节点的节点可能难度较大。我们期望的迭代器设计思路应该类似于图2:
组合枚举器系列的最初设计
这些类使用方法名hasNext()和next(),以便于工作ComponentIterator类实现java.util包中的Iterator接口。
这种设计表明枚举器类构造器将接收一个枚举对象。实际上,这个对象是组合对象,诸如机器组合对象或者流程组合对象。该设计使用一个visited变量来跟踪我们已经列举的节点。这样做可以防止当组合结构有循环时,程序不会陷入无穷循环中。类层次结构顶部的ComponentIterator类的代码如下所示:
package com.oozinoz.iterator;
import java.util.*;
public abstract class ComponentIterator implements Iterator { protected Object head;
protected Set visited;
protected boolean returnInterior = true;
public ComponentIterator(Object head,Set visited){
this.head = head;
this.visited = visited;
}
public void remove(){
throw new UnsupportedOperationException( "componentIterator.Remove");
}
}
这个类把大多数艰难的工作留给子类去完成。
在CompositeIterator子类中,我们需要使用列表迭代器来列举组合节点的子对象。这是图1中标识为1的枚举,在图2中用children变量来表示。组合结构也需要图1中标识为2的枚举器。图2中的subiterator变量可以满足这个需求。CompositeIterator类构造器可以像下面代码那样实例化子枚举器:
public CompositeIterator( Object head,List components,Set visited){
super(head,visited);
children = components.iterator();
}
当开始某组合结构的枚举处理过程时,我们知道第一个返回的节点是头节点(图1中标识为H)。因此,CompositeIterator类的next()方法看起来类似于下面的代码:
public Object next(){
if(peek != null){
Object result = peek;
peek = null;
return result;
}
if(!visited.contains(head)){
visited.add(head); return head;
}
return nextDescendant();
}
next()方法使用visited集合来记录该枚举器是否已经返回头节点。如果该枚举器已经返回组合结构的头节点,nextDescendant()方法必须查找下一个节点。
在任何时间,subiterator变量都可以枚举自身是组合节点的子节点。如果枚举器是有效的,CompositeIterator类的next()方法可以“移动”这个子迭代器。如果子迭代器不能移动,代码必须移动到children列表的下一个元素,为其分配一个子枚举器,并移动这个枚举器。nextDescendant()方法的代码如下所示:
public Object nextDescendant(){ while(true){ if(subiterator != null){ return subiterator.next(); } if(!children.hasNext()) return null; ProcessComponent pc = (ProcessComponent) children.next(); if(!visited.contains(pc)){ subiterator = pc.iterator(visisted); } } }
根据枚举的对象类型不同,该方法本身存在一定限制:代码要求组合结构的子对象实现iterator(s:Set)方法。考虑组合结构的一个例子,比如第5章介绍的ProcessComponent类层次结构。图3给出Oozinoz应用程序用于模拟制造各种焰火制品的工作流的过程组合对象类层次。
Oozinoz公司制造过程流是组合结构