在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")
複製代碼