🔥 核心
组合模式将对象组合成树状的层次结构。
组合模式使得用户对简单元素和复杂容器的使用具有一致性。
🙁 问题场景
你正在为一家商场开发计价程序,用来计算一名顾客所购买商品的总价。
这家商场使用 袋子(Bag)
来装 商品(Product)
。一个袋子中可以装若干个商品或若干个小袋子,小袋子中又可以装若干个商品或若干个小小袋子…以此类推。
商品(Product)
上会带有价格标签,而 袋子
上不会有任何信息——你只有打开这个袋子,才能知道里面装的是什么。此时你会如何计算一个顾客所购买的商品的总价格呢?
不难想到,如果是在现实生活中,我们会从第一个大袋子开始,遇到商品就累计价格,遇到袋子就再次打开袋子…没错,我们可以且只能这么做。
可是,这是售货员人工就能做的事情。但此时你在编写程序,如何让程序完成这个动作呢?写一个个的for循环吗?显然是不可以的,因为你不知道嵌套的层数,即不知道写几层for循环。
🙂 解决方案
作为一名有经验的开发者,你立即反应过来——这是个「递归」的过程!
如果把每个袋子中的东西都掏出来,那么就会得到一个树状的层次结构;我们计算商品总价,就是要遍历到树的所有叶子节点。这听起来简单,但不要忘记一个重要的问题——简单的 商品
,复杂的 袋子
,这是两个类,而我们熟悉的 树(Tree)
,是只有一种 节点(TreeNode)
的。
组合模式建议给简单的 商品
、复杂的 袋子
建立一个统一的接口,使得它们在树上可以视为等价的节点,即「简单元素和复杂容器的使用具有一致性。」
这样,你既不需要了解整个树的结构,也不需要了解对象究竟是简单的 商品
还是复杂的 袋子
,你只需要对通用接口的方法进行统一的调用即可,而且这个方法的调用会沿着树结构传递下去。
嗯嗯,你不仅成为了一名优秀的开发者,还成为了一名优秀的收银员!
🌈 有趣的例子
森林里一棵树可以看成 树枝(Branch)
和 树叶(Leaf)
的组合。树枝上可能有若干树枝和若干树叶,依次类推。另外,树枝(Branch)
和 树叶(Leaf)
具有一个统一的接口:树节点(TreeNode)
。
这个树吸引了一群可爱的 七星瓢虫(LadyBird)
。它们有一个习性,只会落到 树叶
上休息,而不会落到 树枝
上。下面,我们来数一数七星瓢虫的数量。
树节点(统一接口)
interface TreeNode {
void add(TreeNode node);
void remove(TreeNode node);
int countLadybird();