组合模式入门

组合模式允许将对象组合成树形结构来表现”整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

组合模式牺牲单一责任设计原则换取透明性(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

/*
在公司结构中,一个大部门可以直接包含部分直属员工,也可以包含其他子部门
子部门中同样可以包含直属员工和更细分的子部门
这就形成了一个树形结构
*/


/// 员工类和部门类都需要实现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
//公司,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
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