组合模式允许将对象组合成树形结构来表现”整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式牺牲单一责任设计原则换取透明性(transparency), 让组件的接口同时包含一些管理子节点和叶子节点的操作,使用者就可以将组合和叶子结点一视同仁, 也就是说, 一个元素究竟是组合还是叶子节点,对使用者是透明的.
组合模式中涉及到的角色:
Component: 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为
Leaf: 叶子结点对象,没有子结点
Composite: 存储Composite和Leaf两种类型的子部件,实现与子部件有关操作,如增加(add)和删除(remove)等
在以下情形时,考虑使用组合模式:
想获取对象抽象的树形表示
想让客户端同意处理组合结构中的所有对象
UML类图
代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 /* 在公司结构中,一个大部门可以直接包含部分直属员工,也可以包含其他子部门 子部门中同样可以包含直属员工和更细分的子部门 这就形成了一个树形结构 */ /// 员工类和部门类都需要实现EmployeeComponent接口,调用某些非共有操作需要抛出异常 /// 这样如果员工类和部门类如果不支持某个操作,那它们就不需要做任何事情 enum EmployeeError: Error { case UnSupportedOperation } /// 抽象类,实现了员工类和部门类的所有方法 class EmployeeComponent { /// 组合方法: 新增,删除,获取组件 func add(component: EmployeeComponent) throws -> Void { throw EmployeeError.UnSupportedOperation } func remove(component: EmployeeComponent) throws -> Void { throw EmployeeError.UnSupportedOperation } func getChild(index: Int) throws -> EmployeeComponent { throw EmployeeError.UnSupportedOperation } /// 操作方法 func getName() throws -> String { throw EmployeeError.UnSupportedOperation } func getNumber() throws -> Int { throw EmployeeError.UnSupportedOperation } func introduce() throws -> Void { throw EmployeeError.UnSupportedOperation } } /// 员工类,包含姓名和工号两个属性 class Employee: EmployeeComponent { var name: String var number: Int init(name: String, number: Int) { self.name = name self.number = number } /// 覆盖EmployeeComponent的部分方法 override func getName() throws -> String { return name } override func getNumber() throws -> Int { return number } override func introduce() throws { print("Hello, My name is " + name + "My job number is \(number)") } } /// 部门类 class Department: EmployeeComponent { /// 部门名称 var name: String /// 部门可以有任意数量的子部门和直属员工 var employees = NSMutableArray() init(name: String) { self.name = name } /// 覆盖EmployeeComponent的部分操作方法 override func getName() throws -> String { return name; } //使用迭代器模式打印出部门所有子信息 override func introduce() throws { print("This is \(name) department") let iterator = employees.objectEnumerator() var obj = iterator.nextObject() while obj != nil { try (obj as! EmployeeComponent).introduce() obj = iterator.nextObject() } } /// 覆盖EmployeeComponent的组件方法 override func add(component: EmployeeComponent) throws { employees.add(component) } override func remove(component: EmployeeComponent) throws { employees.remove(component) } override func getChild(index: Int) throws -> EmployeeComponent { guard index < employees.count && index >= 0 else { throw EmployeeError.UnSupportedOperation } return employees[index] as! EmployeeComponent } }
客户端调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 //公司,CEO let company = Department(name: "devzhang.cn") let ceo = Employee(name: "zhang", number: 1) try company.add(component: ceo) //市场部 let marketing = Department(name: "Marketing Department") let marketingEmployee1 = Employee(name: "张三", number: 2) let marketingEmployee2 = Employee(name: "李四", number: 3) try? marketing.add(component: marketingEmployee1) try? marketing.add(component: marketingEmployee2) try? marketing.introduce() //研发部 let develop = Department(name: "Develop Department") let developEmployee1 = Employee(name: "赵六", number: 4) let developEmployee2 = Employee(name: "孙七", number: 5) try? develop.add(component: developEmployee1) try? develop.add(component: developEmployee2) try? develop.introduce() /// 异常处理 do { try developEmployee1.add(component: developEmployee2) }catch { print(error) }
输出结果:
1 2 3 4 5 6 7 8 This is Marketing Department department Hello, My name is 张三My job number is 2 Hello, My name is 李四My job number is 3 This is Develop Department department Hello, My name is 赵六My job number is 4 Hello, My name is 孙七My job number is 5 UnSupportedOperation