组合形式
组合形式是一种结构型规划形式, 你可以运用它将目标组合成树状结构, 并且能像运用独立目标一样运用它们。
遇到的问题
假如运用的中心模型能用树状结构表明, 在运用中运用组合形式才有价值。
例如, 你有两类目标: 产品
和 盒子
。 一个盒子中可以包括多个 产品
或许几个较小的 盒子
。 这些小 盒子
中同样可以包括一些 产品
或更小的 盒子
, 以此类推。
假定你希望在这些类的基础上开发一个定购系统。 订单中可以包括无包装的简略产品, 也可以包括装满产品的盒子…… 以及其他盒子。 此刻你会怎么核算每张订单的总价格呢?
订单中可能包括各种产品, 这些产品放置在盒子中, 然后又被放入一层又一层更大的盒子中。 整个结构看上去像是一棵倒过来的树。
你可以测验直接核算: 翻开一切盒子, 找到每件产品, 然后核算总价。 这在实在世界中或许可行, 但在程序中, 你并不能简略地运用循环句子来完结该作业。 你有必要事先知道一切 产品
和 盒子
的类别, 一切盒子的嵌套层数以及其他繁杂的细节信息。 因而, 直接核算极不方便, 乃至完全不可行。
解决方案
组合形式建议运用一个通用接口来与 产品
和 盒子
进行交互, 并且在该接口中声明一个核算总价的办法。
那么办法该怎么规划呢? 关于一个产品, 该办法直接回来其价格; 关于一个盒子, 该办法遍历盒子中的一切项目, 问询每个项目的价格, 然后回来该盒子的总价格。 假如其间某个项目是小一号的盒子, 那么当时盒子也会遍历其间的一切项目, 以此类推, 直到核算出一切内部组成部分的价格。 你乃至可以在盒子的终究价格中添加额定费用, 作为该盒子的包装费用。
组合形式以递归办法处理目标树中的一切项目
该办法的最大长处在于你无需了解构成树状结构的目标的详细类。 你也无需了解目标是简略的产品仍是杂乱的盒子。 你只需调用通用接口以相同的办法对其进行处理即可。 当你调用该办法后, 目标会将恳求沿着树结构传递下去。
实在世界类比
部队结构的比如。
大部分国家的戎行都选用层次结构管理。 每支部队包括几个师, 师由旅构成, 旅由团构成, 团可以持续划分为排。 最终, 每个排由一小队实实在在的战士组成。 军事指令由最高层下达, 经过每个层级传递, 直到每位战士都知道自己应该遵守的指令。
组合形式结构
-
组件 (Component) 接口描绘了树中简略项目和杂乱项目所共有的操作。
-
叶节点 (Leaf) 是树的根本结构, 它不包括子项目。
一般情况下, 叶节点终究会完结大部分的实际作业, 由于它们无法将作业指派给其他部分。
-
容器 (Container)——又叫 “组合 (Composite)”——是包括叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的详细类, 它只经过通用的组件接口与其子项目交互。
容器接收到恳求后会将作业分配给自己的子项目, 处理中间成果, 然后将终究成果回来给客户端。
-
客户端 (Client) 经过组件接口与一切项目交互。 因而, 客户端能以相同办法与树状结构中的简略或杂乱项目交互。
伪代码
在本例中, 咱们将凭借组合形式协助你在图形编辑器中实现一系列的几许图形。
几许形状编辑器示例。
组合图形
CompoundGraphic是一个容器, 它可以由多个包括容器在内的子图形构成。 组合图形与简略图形具有相同的办法。 可是, 组合图形本身并不完结详细作业, 而是将恳求递归地传递给自己的子项目, 然后 “汇总” 成果。
经过一切图形类所共有的接口, 客户端代码可以与一切图形互动。 因而, 客户端不知道与其交互的是简略图形仍是组合图形。 客户端可以与非常杂乱的目标结构进行交互, 而无需与组成该结构的实体类紧密耦合。
组合形式适合运用场景
假如你需求实现树状目标结构, 可以运用组合形式。
组合形式为你供给了两种共享公共接口的根本元素类型: 简略叶节点和杂乱容器。 容器中可以包括叶节点和其他容器。 这使得你可以构建树状嵌套递归目标结构。
假如你希望客户端代码以相同办法处理简略和杂乱元素, 可以运用该形式。
组合形式中界说的一切元素共用同一个接口。 在这一接口的协助下, 客户端不用介意其所运用的目标的详细类。
实现办法
-
保证运用的中心模型可以以树状结构表明。 测验将其分解为简略元素和容器。 记住, 容器有必要可以同时包括简略元素和其他容器。
-
声明组件接口及其一系列办法, 这些办法对简略和杂乱元素都有意义。
-
创立一个叶节点类表明简略元素。 程序中可以有多个不同的叶节点类。
-
创立一个容器类表明杂乱元素。 在该类中, 创立一个数组成员变量来存储关于其子元素的引证。 该数组有必要可以同时保存叶节点和容器, 因而请保证将其声明为组合接口类型。
实现组件接口办法时, 记住容器应该将大部分作业交给其子元从来完结。
-
最终, 在容器中界说添加和删除子元素的办法。
记住, 这些操作可在组件接口中声明。 这将会违反接口阻隔原则, 由于叶节点类中的这些办法为空。 可是, 这可以让客户端无差别地拜访一切元素, 即使是组成树状结构的元素。
组合形式优缺点
-
你可以利用多态和递归机制更方便地运用杂乱树结构。
-
开闭原则。 无需更改现有代码, 你就可以在运用中添加新元素, 使其成为目标树的一部分。
-
关于功能差异较大的类, 供给公共接口或许会有困难。 在特定情况下, 你需求过度一般化组件接口, 使其变得令人难以了解。
代码示例
- Golang 组合形式讲解和代码示例 – 掘金 ()