作者:冯睿    本文选自:赛迪网  2003年01月21日

 

自从J2EE出现以来,就大大简化了在Java下的企业级开发。但是随着J2EE越来越普遍地被应用到各个领域中,开发者们渐渐意识到需要一种方法来标准化应用程序的开发过程,他们采用的方法是标准化应用程序的结构层。在结构层通常封装了一些独立于业务逻辑的复杂技术,以便在业务逻辑和底层的架构之间建立起弱连接。无可否认,J2EE是一个很成功的技术,它为一些基本的任务提供了一致的标准,例如数据库连接、分布式应用程序等。但是使用J2EE并不能保证开发人员开发出成功的应用程序。有些人认为J2EE本身就是一种框架技术,但是这种认识是不正确的,我们应该意识到J2EE并没有提供一个能够帮助开发人员开发出高质量应用程序的框架,因此很多有经验的开发人员通过利用设计模式来弥补这一缺陷。

在开发人员的圈子中,大家通过相互交流在开发过程中所遇到的问题以及解决方法来丰富整个圈子的经验。而设计模式就是在这样的情况下产生的。一个设计模式必然是针对某个特定的问题的,这个问题的解决方案以及这样解决问题产生的后果。在本文中我将讨论复合模式的特点和它的应用。

复合模式(Composite Patten)介绍


在介绍复合模式前,我们需要定义一下什么是复合对象(Composite Object)。复合对象是包含了其它对象的对象。例如,一幅图由一些基本的对象组成,例如线、圆、矩形和文本等,因此图就是复合对象。因为在Java中,开发人员操作基本对象的方式和操作复合对象的方式常常相同,因此需要利用到复合模式。例如,线或文本等基本图形对象都需要支持绘制、移动或缩放等功能;而图这种复合对象也需要支持相同的功能。在理想的情况下,我们希望对复合对象和基本对象以完全相同的方式完成这些操作,否则实现的代码将会产生不必要的复杂性,并且不易于维护和扩展。

那么什么是复合模式呢?将对象组织到树结构中以表达部分整体的层次关系就实现了复合模式,它使程序能够以相同的方式对待基本对象和复合对象。

在程序中实现复合模式并不难。复合类继承一个代表基本类型的基类就可以了。图1显示了一个表现复合模式思想的类图。



图1 复合模式的实现


在图1中,Component是代表基本类型的基类(也可以是一个接口);Composite是复合类。例如Component代表的是基本图形元素的基类,而Composite代表的是图;Operation1()和Operation2()方法分别是移动和缩放操作。图1中的Leaf类代表的是点、线或者圆等基本图形元素。

针对Component类中的每个方法,Composite类都有相同名称的方法与之对应。Composite类保存了一个基本对象的集合。通常Composite类中的方法在实现时都会将集合中的对象遍历一次,然后调用每个对象中相应的方法。例如图对象Drawing的draw()方法可能是这样实现的:


// 代码1一个复合方法
public void draw() {
   // I遍历所有的对象
   for(int i=0; i < getComponentCount(); ++i) {
      // 获得对对象的应用,调用它的draw方法
      Component component = getComponent(i);
      component.draw();   
   }
}


由于Composite类继承了Component类,因此你可以将一个Composite对象传递给需要Component对象作为参数的方法。例如:


// 代码2  repaint方法
public void repaint(Component component) {
   // 事实上component可能是一个复合对象,因此该方法没有区分基本对象和复合对象
   component.draw();
}


上面的repaint()方法中,Component对象被作为参数传递到了函数体,这个对象可以是Component,也可以是Composite。然后函数体中调用draw()方法。由于Composte类继承了Component,程序就不用区分传入的参数到底是哪种类的实例,只需要调用该对象的draw()方法就可以了。

图1中的类图展示了复合模式的一个方面:开发人员必须在引用一个Component对象时必须区分它到底了一个Component对象还是一个Composite对象。通常开发人员可以在Component类中加入一个方法,例如isComposite(),来辨别Composite类。如果该方法返回的是True,开发人员就需要将Component对象强制转换为Compoiste对象。


// 代码3,区分Component和Composite
...
if(component.isComposite()) {
   Composite composite = (Composite)component;
   composite.addComponent(AComponent);
}
...


图2显示了另一种实现复合模式的方法:



图2 另一种复合模式的实现方法


在图2所示的复合模型中,开发人员不必区分Component对象和Composite对象,也不需要将Component对象强制转换Composited对象。这样代码3中的代码就变成了一行:


// 代码4,不区分Component和Composite
...
component.addComponent(AComponent);
...


但是如果代码4中的component不是Composite的实例,addComponent()方法会做些什么呢?这是图2中的复合模式中最重要的一部分。显然一个基本类型不可能包含其他基本类型或复合类型,Component.addComponent()可能什么也不干,或者抛出一个异常。通常我们认为将基本类型加入其他基本类型的操作是一个错误,因此抛出异常是开发人员最好的选择。

那么这两种复合模式的实现方法哪一个更好呢?这是一个常常引起争论的问题。我个人觉得第二种方法更好一些,因为开发人员不必区分Component对象和Composite对象,也不用强制转换对象。