在WWDC15上,苹果宣布Swift是世界上第一门面向协议编程(POP)语言。相比与传统的面向对象编程 (OOP),POP 显得更加灵活。RxSwift、ReactorKit 核心也是面向协议编程的。编程
要弄清楚什么是面向协议(POP),咱们应该先知道什么是Swift
协议? 咱们定义一个简单的Swift协议以下:swift
protocol Runable {
var name: String { get }
func run()
}
复制代码
代码中定义名为Runable
协议,包含一个name
属性,以及一个run
方法的定义 所谓协议,就是一组属性和/或方法的定义,而若是某个具体类型想要遵照一个协议,那它须要实现这个协议所定义的全部这些内容。协议实际上作的事情不过是"关于实现的约定"。bash
面向协议编程(POP)是Swift2.0
引入的一种新的编程范式。POP
就是经过协议扩展,协议继承和协议组合的方式来设计须要编写的代码。组件化
Swift是一门面向对象的语言,类已经知足咱们全部的需求,功能也十分强大。为何还要使用POP?单元测试
首先在Swift
中,值类型优先于类。然而,面向对象的概念不能很好地与结构体和枚举一块儿工做: 由于结构体和枚举不可以被继承。所以,做为面向对象的一大特征—继承就不可以应用于值类型了。测试
再者,实际开发工程中,咱们常常会遇到以下场景: 假设咱们有一个ViewController
,它继承自UIViewController
,咱们向其新添加一个方法 customMethod
:ui
class ViewController: UIViewController {
//新添加
func customMethod() {
}
}
复制代码
这个时候咱们有另一个继承自UITableViewController
的OtherViewController
,一样也须要向其添加方法customMethod
spa
class OtherViewController: UITableViewController {
//新添加
func customMethod() {
}
}
复制代码
这里就存在一个问题:很难在不一样继承关系的类里共用代码。 咱们的关注点customMethod
位于两条继承链 UIViewController -> ViewCotroller
和 UIViewController -> UITableViewController -> AnotherViewController
的横切面上。面向对象是一种不错的抽象方式,可是确定不是最好的方式。它没法描述两个不一样事物具备某个相同特性这一点。在这里,特性的组合要比继承更贴切事物的本质。设计
这种状况,咱们有以下几种方法解决:code
Copy & Paste 这是一个糟糕的解决方案,咱们应该尽可能避免这种作法。
BaseViewController 在一个继承自 UIViewController
的 BaseViewController
上添加须要共享的代码,或者在 UIViewController
上添加 extension
。这是目前不少人经常使用的解决方法,可是若是不断这么作,会让所谓的BaseViewController
很快变成垃圾堆。职责不明确,任何东西都能扔进 Base,你彻底不知道哪些类走了 Base,而这个“超级类”对代码的影响也会不可预估。
依赖注入 经过外界传入一个带有 customMethod
的对象,用新的类型来提供这个功能。这是一个稍好的方式,可是引入额外的依赖关系,可能也是咱们不太愿意看到的。
多继承 固然,Swift 是不支持多继承的。不过若是有多继承的话,咱们确实能够从多个父类进行继承,并将customMethod
添加到合适的地方,但这又会带来其余问题。
总的来讲,面向协议编程(POP) 带来的好处以下:
定义一个含有customMethod
的协议ex
:
protocol ex {
func customMethod();
}
复制代码
在实际类型遵照这个协议:
extension ViewController :ex {
func customMethod() {
//
}
}
extension OtherViewController : ex {
func customMethod() {
//
}
}
复制代码
这样的实现和Copy & Paste
的方式同样,必需要在ViewController
和OtherViewController
都写一遍。有什么方法能够解决这问题呢?那就是协议扩展
能够在 extension ex
中为 customMethod
添加一个实现:
extension ex {
func customMethod() {
//
}
}
复制代码
有这个协议扩展,只须要声明ViewController
和OtherViewController
遵循ex
,就能够直接使用customMethod
了。
协议扩展:
protocol Entity {
var name: String {get set}
static func uid() -> String
}
extension Entity {
static func uid() -> String {
return UUID().uuidString
}
}
struct Order: Entity {
var name: String
let uid: String = Order.uid()
}
let order = Order(name: "My Order")
print(order.uid)
复制代码
协议继承
协议能够从其余协议继承,而后在它继承的需求之上添加功能,所以能够提供更细粒度和更灵活的设计。
protocol Persistable: Entity {
func write(instance: Entity, to filePath: String)
init?(by uid: String)
}
struct InMemoryEntity: Entity {
var name: String
}
struct PersistableEntity: Persistable {
var name: String
func write(instance: Entity, to filePath: String) { // ...
}
init?(by uid: String) {
// try to load from the filesystem based on id
}
}
复制代码
协议的组合
类、结构体和枚举能够符合多个协议,它们能够采用多个协议的默认实现。这在概念上相似于多继承。这种组合的方式不只比将全部须要的功能压缩到一个基类中更灵活,并且也适用于值类型。
struct MyEntity: Entity, Equatable, CustomStringConvertible {
var name: String
// Equatable
public static func ==(lhs: MyEntity, rhs: MyEntity) -> Bool {
return lhs.name == rhs.name
}
// CustomStringConvertible
public var description: String {
return "MyEntity: \(name)"
}
}
let entity1 = MyEntity(name: "42")
print(entity1)
let entity2 = MyEntity(name: "42")
assert(entity1 == entity2, "Entities shall be equal")
复制代码