面向协议编程 (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
继承一些用不到的属性和方法,那么按照面向对象的思想,我们应该在Teacher
、Cook
与 Person
中间增加一层:
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,他们各有长处, 应该配合来使用。