前面提到的非树状Composite模式纯属建模失误,它把machine同时标识为plant和bay的一部分。对于物理对象而言,你可能希望不允许把一个对象包含在多个对象中。然而,问题域可能会包含非物理的对象,在这种情况下,环状的包含关系是有意义的。在对加工流程进行建模的时候,这种情况会经常出现。比如说在焰火弹的生产过程中,如果检查不合格则有可能返工。
图5-5 生产焰火制品的流程包含一些交替的步骤和顺序的步骤
下图描述了代表制作高空焰火弹的流程的对象。make流程包含biuldInner步骤、inspect步骤以及reworkOrFinish子流程。reworkOrFinish子流程具有两种可选的执行路径。make流程的最后可能是disassemble步骤,也可能是finish步骤。
5-6 描述了Oozinoz高空焰火弹生产流程的对象模型
ProcessComponent层次结构中的getStepCount()方法用于统计流程图中单个流程的步骤数。注意,并不是统计流程的深度数目,而是流程对象图中叶节点的流程步骤数。该方法的处理必须小心,只能对每个步骤统计一次,并且在流程中包含环的时候要防止进入死循环。ProcessComponent类实现getStepCount()方法,以便依赖该方法来获取访问过的节点列表:
public int getStepCount() { return getStepCount(new HashSet()); } public abstract int getStepCount(s:Set);
ProcessComposite类的getStepCount()方法会小心避免去访问一个先前已经访问过的节点。
public int getStepCount(Set visited) { visited.add(getName()); int count = 0; for(int i=0;i<subprocesses.size();i++) { ProcessComponet pc = (ProcessComponent)subprocesses.get(i); if(!visited.contains(pc.getName())) count += pc.getStepCount(visited); } return count; }
ProcessStep类的getStepCount()方法比较简单:
public int getStepCount(Set visited) { visited.add(name); return 1; }
com.oozinoz.process包中含有一个ShellProcess类,该类的make()方法能够返回图5-6所示的make类对象。com.oozinoz.testing包中有一个ProcessTest类,该类提供各种流程图的自动测试方法。例如:ProcessTest类包含有一个方法,用来测试getStepCount()方法是否能够正确统计具有环状结构的make流程对象中的步骤数目:
public void testShell() { assertEquals(4,ShellProcess.make().getStepCount()); }
该项测试在JUnit测试框架中运行并完成测试。
环的影响:即使组合对象模型不是树状结构,其中的大多数方法,如统计叶节点数目的方法,仍然是十分有意义的。一般而言,非树状组合对象模型与树状组合对象模型仅有一点不同,即在对非树状组合对象模型的节点进行操作的时候,必须十分小心,以免对一个节点操作两次。不过,当组合对象模型包含环的时候,某些方法会变得没有意义。例如,由于返工的步骤可能会重复任意次,因此我们不可能通过算法确定Oozinoz公司制作高空焰火弹的最大步骤数目。在组合对象模型中,任何依赖于路径长度的操作在组合对象模型包含环的时候都是没有意义的。因此,虽然我们讨论树的深度----从根节点到叶节点的最长路径,但是在一个包含有环的图中路径是没有最大深度值的。
允许非树状组合对象模型的另一个后果是不能假定每一个节点只有一个父节点。如果组合对象模型并非树状结构,那么有的节点可能会有多个父节点。图5-6的生产流程对象模型可能会有多个组合步骤类引用inspect步骤,而inspect对象就有多个父节点。一个节点拥有多个父节点本身并没有什么问题,但是,我们在建模和编写代码时必须考虑这个问题。
Composite(组合)模式小结:Composite模式包含两个相关的重要概念。其中一个概念是群组可以包含个体对象,也可以包含其他群组。与此相关的另一个概念是群组一个体对象可以共享同一个接口。将这些概念应用于对象建模,就可以创建一个抽象类或Java接口来定义群组对象和个体对象的公共特性。
组合对象模型中的方法通常采用递归定义。由于递归的存在,在编写代码的时候需要谨防死循环。不过,只要通过采取措施组合对象模型为树状结构,就可以避免这类问题。另外,组合对象模型中也可以出现环。但是我们必须修改算法来监视无穷尽的递归。