15.2 解 决 方 案

15.2.1 使用组合模式来解决问题

用来解决上述问题的一个合理的解决方案就是组合模式。那么什么是组合模式呢?

1.组合模式的定义

将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

2.应用组合模式来解决问题的思路

仔细分析上面不用模式的例子,要区分组合对象和叶子对象的根本原因,就在于没有把组合对象和叶子对象统一起来。也就是说,组合对象类型和叶子对象类型是完全不同的类型,这导致了操作的时候必须区分它们。

组合模式通过引入一个抽象的组件对象,作为组合对象和叶子对象的父对象,这样就把组合对象和叶子对象统一起来了,用户使用的时候,始终是在操作组件对象,而不再去区分是在操作组合对象还是叶子对象。

提示

组合模式的关键就在于这个抽象类,这个抽象类既可以代表叶子对象,也可以代表组合对象,这样用户在操作的时候,对单个对象和组合对象的使用就具有了一致性。

15.2.2 组合模式的结构和说明

组合模式的结构如图15.1所示。

图片

图15.1 组合模式结构示意图

■ Component:抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。

■ Leaf:叶子节点对象,定义和实现叶子对象的行为,不再包含其他的子节点对象。

■ Composite:组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。

■ Client:客户端,通过组件接口来操作组合结构里面的组件对象。

一种典型的Composite对象结构通常是如图15.2所示的树型结构,一个Composite对象可以包含多个叶子对象和其他的Composite对象。虽然图15.2看起来好像有些对称,但那只是为了让图看起来美观一点,并不是说Composite组合的对象结构就是这样对称的,这点要提前说明一下。

图片

图15.2 典型的Composite对象结构

15.2.3 组合模式示例代码

(1)先来看看组件对象的定义。示例代码如下:

2eb5def923564aa797d5bbd6dd5df028

2db52826b5d6433fb766cda15c0356f6

(2)接下来看看Composite对象的定义。示例代码如下:

4cbd26225060496db0219ab3bd245e06

c0a8f9f74031481ab9f9df5d26934fb1

(3)该来看看叶子对象的定义了。相对而言比较简单。示例代码如下:

4320aca4ccb349ef8a9295c9aa93038d

(4)对于Client,就是使用Component接口来操作组合对象结构,由于使用方式千差万别,这里仅仅提供一个示范性质的使用,顺便当作测试代码使用。示例代码如下:

71c6ff3b6b854a1fa613152e15cae8a1

15.2.4 使用组合模式重写示例

理解了组合模式的定义、结构和示例代码,对组合模式应该有一定的掌握了吧。下面就使用组合模式来重写前面不用模式的示例,看看用组合模式来实现会是什么样子,和不用模式有什么相同和不同之处。

为了整体理解和把握整个示例,先来看看示例的整体结构,如图15.3所示。

图片

图15.3 使用组合模式实现示例的结构示意图

(1)为组合对象和叶子对象添加一个抽象的父对象做为组件对象。在组件对象中,定义一个输出组件本身名称的方法以实现要求的功能。示例代码如下:

b029c6a175be448ebb3a2f2dad7241b5

6b4ef42e6fe4459a83a4eedd0864ffb4

(2)来看看叶子对象的实现,它的变化比较少,只是让叶子对象继承了组件对象,其他的和不用模式相比,没有什么变化。

示例代码如下:

19c4c9edda8c4a7990e588690aeb8d78

(3)接下来看看组合对象的实现,这个对象变化就比较多,大致有如下的改变。

■ 新的Composite对象需要继承组件对象。

■ 原来用来记录包含其他组合对象的集合和包含其他叶子对象的集合,被合并成为一个,就是统一的包含其他子组件对象的集合。使用组合模式来实现,不再需要区分到底是组合对象还是叶子对象了。

■ 原来的addComposite和addLeaf方法,可以不需要了,将其合并实现成组件对象中定义的addChild方法,但是需要现在的Composite来实现这个方法。使用组合模式来实现,不再需要区分到底是组合对象还是叶子对象了。

■ 原来的printStruct方法的实现,完全要按照现在的方式来写,变化较大。

具体的示例代码如下:

6a6a5aa7906a470092f4da3bfd765310

63238dc983c845c485c7cef5477b7912

7a2ab5bf81764835a00d8cd08fe7ba2b

(4)客户端也有变化。客户端不再需要区分组合对象和叶子对象了,统一使用组件对象,调用的方法也都要改变成组件对象定义的方法。示例代码如下:

5c6ce5de4b244cf5a185f2b6a1d32745

从上面的示例,大家可以看出,通过使用组合模式,把一个“部分—整体”的层次结构表示成了对象树的结构。这样一来,客户端就无需再区分操作的是组合对象还是叶子对象了;对于客户端而言,操作的都是组件对象。