我们已学习了设计模式的行为类型,从这一篇开始学习设计模式的结构类型,即关于对象之间关系的设计模式。
第一篇我们学习复合模式(Composite Pattern),在需要表示和操作对象层次结构的情况下使用此模式。
问题提出
看一下苹果电脑Mac下的KeyNote应用,它类似微软的PowerPoint。
我们将两个矩形组成一组,两个圆形也组成一组。
我们移动组,组内的对象全部一起移动,调整组的大小,组内的对象也全部进行调整。我们像处理单独的对象一样来处理组。
我们创建层次结构如下。
另一个典型的例子是文件系统,文件系统有文件和文件夹,文件夹包含文件,当我们删除、移动文件夹时,文件夹里的文件也被删除和移动了。
我们尝试创建代码实现。
新建类Shape,它有一个render方法,每个Shape对象都有将自己渲染到屏幕上的能力。
新建类Group,它有0个或多个Shape对象,有add方法以及render方法,在render方法里,遍历每一个Shape对象调用其render方法。
我们可以向Shape和Group添加move、resize等方法,在Group里,同样需要遍历每一个对象并进行调用。
这里保持简单,仅用render方法。
到main方法里进行测试。
创建group1,添加2个shape对象到该组。
创建group2,添加2个shape对象到该组。
再将group1和group2合并到组group。
我们看到,在group调用add方法尝试将组(Group)合并时就出错了。
Shape和Group是不通用的。
想要解决这个问题,我们回Group类,替换Shape列表为Object列表,Object是所有类的基类,可以通用。在render方法里,先进行类型的判断,然后转换成相应的类型调用render方法。
继续完成main方法里的测试。
坦率讲,这种实现方法非常丑陋,如果我们有另外的方法比如move和resize,我们都需要重复这种先判断类型再进行类型转换的结构。
复合模式(Composite Pattern)用于解决这种状况,利用该模式,我们可以使用一致的方法处理组和对象。
解决方案
当前我们的结构是这样。
为了能一致处理 对象(Shape)和容器(Group),我们需要提取出这两种类型的共同点放入基类或接口。
他们的共同点是什么?
render方法!
我们将其提取出来并引入一个接口叫Component。
Group类中,用Component列表代替Object列表。
非常简单。
使用这种结构,我们不再需要对类型进行检查并显式地转换成Shape或Group,我们直接调用render即可。
这个就叫做复合模式(Composite Pattern)。
当我们希望在处理层次结构的对象时,不管他们是容器或对象,都用同样的方法处理,就可以使用复合模式。
就像前面说过的,文件系统是另一个很好的例子。
四人帮的结构是这样的。
Leaf相当于我们的Shape,Composite相当于我们的Group,Composite可以由0或多个Component组成。
代码实现
创建接口Component,它定义了render方法。
Shape实现接口Component。
Group类也实现Component,并用Component列表代替原有的Object列表。
在render方法,不再判断列表项目,直接调用render方法即可。
明天,我们要添加方法move,也非常简单。
先在Component定义move方法,再在Shape和Group作相关实现即可。
这里的重点是,在render方法或move方法里,我们根本无需关心它是Shape或Group,处理方法都是一致的。
最后到main方法测试。
总之,我们可以用复合模式来表示对象的层次结构,并以相同的方式处理此层次结构中的对象。
小结
复合模式可以用来表示对象的层次结构,并以相同的方式处理此层次结构中的对象。