0%

面向协议编程

面向协议编程 (Protocol Oriented Programming,以下简称 POP) 是 Apple 在 2015 年 WWDC 上提出的 Swift 的一种编程范式。相比与传统的面向对象编程 OOP,POP 显得更加灵活。

Swift 的标准库使用了大量的协议,整个iOS/MacOS库使用了大量协议来搭建框架。

面向对象的特点

构建一个模块的框架,在面向对象中会抽取出公共父类

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
class Person {
var age = 0
var name = ""

func sayName() {
print("my age is \(name)")
}

func eat() {
print("eat food.")
}
}


class Student: Person {
override func eat() {
print("eat Person meal")
}
}

class Teacher: Person {
var salary = 0.0

override func eat() {
print("eat Teacher meal")
}

func getPaid() {
print("this month get paid: \(salary)")
}
}

我们可以看到父类持有共同的属性与方法,子类可以自由决定是否重写,比如 Person 中的 sayName 方法,子类无需再次实现,只需要继承即可使用,增加代码的复用率。

我们还有一个属性 salary 薪资,以及领工资的行为,并不是所有人都有薪资以及领工资的行为,所以目前就单独放在 Teacher 中。

这个时候这个模块很完美,没有多余的代码,结构清晰。

接下来某一天新增一个需求,需要一个厨师,也有 Person 的所有特质,那么理所当然应该继承自 Person

1
2
3
4
5
6
class Cook: Person {
override func eat() {
print("Cook eat")
}
func Salary() { }
}

但是厨师也应该有薪资以及领工资行为,从Teacher复制一份到Cook显然不是我们想要的,如果放在Person里共用,又会超出Person应该承受的范围,会导致比如Student继承一些用不到的属性和方法,那么按照面向对象的思想,我们应该在TeacherCookPerson 中间增加一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Workers: Person {
var salary = 0.0
func getPaid() {
print("this month get paid: \(salary)")
}
}

class Teacher: Workers {
override func eat() {
print("eat Teacher meal")
}
}

class Cook: Workers {
override func eat() {
print("Cook eat")
}
}

这样从 OOP 的思想上完美解决问题,但是这种改动会涉及到原先的继承框架,需要改动原来的继承关系,显然会增加不确定性与繁琐性,而且 Swift 中的继承只有单继承,所以如果完全靠 OOP 的范式去维护框架,只有这一条路。

使用协议与扩展解决上诉问题

一个简单的协议与扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protocol PersonProtocol {
var age: Int {set get}
var name: String {set get}

func sayName()
func eat()
}

extension PersonProtocol {
func sayName() {
print("my age is \(name)")
}

func eat() {
print("eat food.")
}
}

所有遵循此协议的类都必须包含对应的属性与方法,协议扩展实现的方法,会变成可选方法,到这里就完全可以用 PersonProtocol 来代替 Person类,再用WorkersProtocol代替 Workers类:

1
2
3
4
5
6
7
8
9
10
protocol WorkersProtocol: PersonProtocol {
var salary: Float { set get }
func getPaid()

}
extension WorkersProtocol {
func getPaid() {
print("this month get paid: \(salary)")
}
}

现在的三种类将会变成:

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
class Student: PersonProtocol {
var age: Int = 0
var name: String = ""

func eat() {
print("eat Person meal")
}
}


class Teacher: WorkersProtocol {
var age: Int = 0
var name: String = ""
var salary: Float = 0.0

func eat() {
print("eat Teacher meal")
}
}

class Cook: WorkersProtocol {
var age: Int = 0
var name: String = ""
var salary: Float = 0.0

func eat() {
print("Cook eat")
}
}

这个时候的类,只需要遵循自己需要的协议,就可以变相继承公共的属性和方法,脱离出 OOP 的框架,对框架的扩展与维护增加的极大的便利。

而且这个时候对于不同的继承系列里相同的特征,也可以用协议来实现代码的复用与框架的扩展合理性

当然并不是要完全抛弃 OOP,他们各有长处, 应该配合来使用。