前几天在《戏说设计模式》这篇文章中看到了一段关于组合模式的描述:
COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。” “……”,MM都会用Composite模式了,你会了没有?
这个例子还是比较通俗的解释了合成模式的。
所谓合成模式就是指将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
定义是不是云雾缭绕让人看不明白,没关系再举例说明。
假设我们要做一块蛋糕(cake),我们可能会经历如下的流程:准备面粉->和面->发酵->切割->烘焙->加奶油->加黄油等等。于是我们就可以说做蛋糕这个任务是由一系列的子任务,比如切割、烘焙等组合而成。这些子任务又可能又更细粒度的子任务组合而成。
纵观这些子任务,他们都有一些共同的属性,比如会花费一定的时间,每个子任务都有自己的名字等等。
再看由这些子任务组合而成的更大的任务。这个大一些的任务本质上也是任务,只不过它是由子任务组合而成,仅此而已。
GoF将组合模式描述为: The sum acts like one of the parts。当你需要构造基于继承关系的对象树,并且希望你的代码将单一对象和组合对象一视同仁时,你最好试试组合模式。
在组合模式总GoF将实现统一接口或你需要实现对象树的基类的对象称作:component。在我们的蛋糕例子里,component应该是所有任务的抽象基类。
继承自component、且无法再进行细粒度细分的类为称为leaf,这个应该很好理解。
那么同理leaf的父节点就是composite了,他是更高层级的component。
下面的代码演示了composite的具体实现,其实现了我们的做蛋糕流程。
class Task
attr_reader :name
def initialize name
@name = name
end
def get_time_required
0.0
end
end
class AddDryIngredientsTask < Task
def initialize
super 'Add dry ingredients'
end
def get_time_required
1.0
end
end
class MixTask < Task
def initialize
super 'Mix that batter up'
end
def get_time_required
3.0
end
end
class MakeBatterTask < Task
def initialize
super 'Make batter'
@sub_tasks = []
add_sub_task AddDryIngredientsTask.new
add_sub_task MixTask.new
end
def add_sub_task task
@sub_tasks << task
end
def remove_sub_task
@sub_tasks.delete task
end
def get_time_required
time = 0.0
@sub_tasks.each {|task| time += task.get_time_required}
time
end
end
在上面的代码中,我们的component就是Task基类。所有的leaf和composite都是继承自该类。Task类实现了统一的接口gettimerequired,表示完成该task需要的时间。
MixTask是leaf,继承自Task且不能进行进一步的细化。
MakeBatterTask是composite。其维护了1个subtasks的子集,所有组成其的子集都保存在其中。另外其提供了一系列的维护subtasks的方法,如新增1个subtask,移除1个subtask。
下面的代码优化了MakeBatterTask类。我们可以直接抽象出Composite类。该类的主要任务是维护属于Composite的subtasks。因为我们可能有若干个Composite类的实例,因此这样做是灵活且有效率的。
class CompositeTask < Task
def initialize name
super name
@sub_tasks = []
end
def add_sub_task task
@sub_tasks << task
end
def remove_sub_task
@sub_tasks.delete task
end
def get_time_required
time = 0.0
@sub_tasks.each {|task| time += task.get_time_required}
time
end
end
class MakeBatterTask < CompositeTask
def initialize
super 'Make batter'
add_sub_task AddDryIngredientsTask.new
add_sub_task MixTask.new
end
end
总的说来合成模式适合表示整体和部分的关系,或者说是零件和模块的关系,若干零件合成一个模块,而从本质上来说模块也可以是一个零件;合成模式可以用来构造树用来表示整体和部分的关系。合成模式将零件和组合而成的模块同等看待。