Swift解决方案:复合

复合模式涉及集合和单个对象的树层次结构。

在上面的插图中,我们有一个文件夹和mp3的目录。 文件夹仅包含文件数组。 每个文件夹的集合可以包含mp3和其他文件夹的混合。 该插图很好地对应于我们定义的每个部分:

  • 层次结构->目录
  • 收集对象->文件夹
  • 单个对象-> mp3文件

它允许客户以相同的方式对待每个层次结构元素。

层次结构中的所有文件共享一个公共接口,而不管它是文件夹还是mp3。 例如,mp3和文件夹都是可以重命名,移动和复制的文件。 因此,可以合理地期望符合要求实现类文件行为的协议。

重要的一点是,客户可以统一对待小组和单个部分。 在使用每个文件之前,无需经常对其进行类型检查。 这是使元素派生自相同协议或基类的直接结果。

UML

组件 :为集合和单个对象提供公共接口的抽象。 树中的所有元素都必须派生自组件协议或基类。

基元 :以前称为“单个对象”。基元只是树中不包含子级成分的成分。

Composite :以前称为“集合对象”。Composite是包含组件数组的对象。 虽然Composite对象和Primitive对象共享相同的接口,但是Composites包含其他方法来管理其子级。

请注意:有几种方法可以引用我们的具体组件:

我个人更喜欢“复合”和“原始”这两个术语。接下来,让我们进入代码吧!

实作

在我们的示例中,我们将向公司部门分配奖金,最终将其发放给员工。 在这种情况下,部门和员工分别充当我们的组合和基元。

零件

protocol Payee { var name: String { get } var bonusAmount: Double { get } func receive(bonus: Double) } 

首先,我们为复合对象和原始对象创建一个Payee协议。 这将是我们统一对待他们的手段。

雇员

 class Employee: Payee { // 1 private var _name: String private var _bonusAmount: Double = 0 var name: String { return _name } var bonusAmount: Double { return _bonusAmount } init(name: String) { self._name = name } // 2 func receive(bonus: Double) { _bonusAmount += bonus } } 

让我们详细看一下我们的代码:

  1. 我们创建一个符合Payee的员工,实现属性namebonusAmount
  2. receive(bonus:)设置为操纵每个员工的奖金金额的唯一方法。

部门

现在让我们实现Department

 class Department: Payee { private var _name: String // 1 private var _bonusAmount: Double { get { var bonus = 0.0 for subunit in subunits { bonus += subunit.bonusAmount } return bonus } set { let splitCount = Double(subunits.count) let splitBonus = newValue / splitCount for subunit in subunits { subunit.receive(bonus: splitBonus) } } } // 2 private let subunits: [Payee] var name: String { return _name } var bonusAmount: Double { return _bonusAmount } init(name: String, subunits: [Payee] = []) { self._name = name self.subunits = subunits } func receive(bonus: Double) { _bonusAmount += bonus } } 

详细地说,这是我们在创建部门类中完成的工作:

  1. 创建一个私有属性_bonusAmount 。 它的获取程序通过访问子部门(子部门和雇员的集合)并返回其奖金金额的总和来计算奖金。 此外,我们还包括一个设置器,该设置器将分配的所有奖金均分并分配给每个子组件。
  2. Department通过收集收款人来充当我们的综合Department

请注意,部门(及其子部门)并不严格持有任何奖金; 他们总是分配奖金,直到奖金到达员工手中。

同样值得重复的是,复合材料不仅符合共享协议。 他们还实现了子组件管理的方法。 具有add(Payee:)remove(Payee:)是可以预期的,但是超出了此示例的范围。 我们仅在初始化期间设置子组件,并通过将子subunits为私有常量来防止进一步修改。

使用复合图案

 // 1 let joan = Employee(name: "Joan") let tom = Employee(name: "Tom") let cleo = Employee(name: "Cleo") let alex = Employee(name: "Alex") // 2 let graphicDesignDepartment = Department(name: "Graphic Design", subunits: [cleo, alex]) let marketingDepartment = Department(name: "Marketing", subunits: [joan, tom, graphicDesignDepartment]) // 3 marketingDepartment.receive(bonus: 1000) 

这是我们逐步执行的操作:

  1. 创建多个员工,其中一些可用于创建部门。
  2. 创建简单的树层次结构:实例化包含图形设计部门的营销部门,并随处散布员工。
  3. 给行销部门$ 1000的奖金。 这将自动分发给其子部门和员工。 子部门进一步将奖金分配和下放,直到达到雇员为止。

用例

与往常一样,重要的是要识别出何时应用该模式。 考虑以下指标:

  • 存在集合和单个对象的树层次结构。
  • 当需要统一对待树中的对象时,与在执行操作之前查询每个组件的类型相反。
  • 当复合和原始共享相似的功能时。
  • 每当需要在整个树层次结构中分配资源时。

结论

你做到了! 您现在可以有效地处理树数据结构。

每个设计模式都是您的编程工具箱中的强大工具。 始终扩展您可用的工具是一项了不起的成就。 恭喜,继续学习直到下一次!

最初发布在 swiftcraft.io上