Swift面向協議編程(POP)

iOS開發2019-09-11 08:39:16

黑客技術
點擊右側關注,瞭解黑客的世界!

Java開發進階
點擊右側關注,掌握進階之路!

Python開發
點擊右側關注,探討技術話題!


作者丨king_jensen
來源丨掘金
https://juejin.im/post/5d68c722f265da03a0498e0c

在WWDC15上,蘋果宣佈Swift是世界上第一門面向協議編程(POP)語言。相比與傳統的面向對象編程 (OOP),POP 顯得更加靈活。RxSwift、ReactorKit 核心也是面向協議編程的。

一、什麼是POP


要弄清楚什麼是面向協議(POP),我們應該先知道什麼是Swift協議?我們定義一個簡單的Swift協議如下:

protocol Runable {
    var name: String { get }
    func run()
}

代碼中定義名為Runable協議,包含一個name屬性,以及一個run方法的定義 所謂協議,就是一組屬性和/或方法的定義,而如果某個具體類型想要遵守一個協議,那它需要實現這個協議所定義的所有這些內容。協議實際上做的事情不過是"關於實現的約定"。

面向協議編程(POP)是Swift2.0引入的一種新的編程範式。POP就是通過協議擴展,協議繼承和協議組合的方式來設計需要編寫的代碼。

二、為什麼要使用面向協議編程(POP)


Swift是一門面向對象的語言,類已經滿足我們所有的需求,功能也十分強大。為什麼還要使用POP?

首先在Swift中,值類型優先於類。然而,面向對象的概念不能很好地與結構體和枚舉一起工作: 因為結構體和枚舉不能夠被繼承。因此,作為面向對象的一大特徵—繼承就不能夠應用於值類型了。

再者,實際開發工程中,我們經常會遇到如下場景: 假設我們有一個ViewController,它繼承自UIViewController,我們向其新添加一個方法 customMethod

class ViewController: UIViewController {
    //新添加
    func customMethod() {
    }
}

這個時候我們有另外一個繼承自UITableViewControllerOtherViewController,同樣也需要向其添加方法customMethod

class OtherViewController: UITableViewController {
    //新添加
    func customMethod() {
    }
}

這裏就存在一個問題:很難在不同繼承關係的類裏共用代碼。我們的關注點customMethod位於兩條繼承鏈UIViewController -> ViewCotroller
UIViewController -> UITableViewController -> AnotherViewController的橫切面上。面向對象是一種不錯的抽象方式,但是肯定不是最好的方式。

它無法描述兩個不同事物具有某個相同特性這一點。在這裏,特性的組合要比繼承更貼切事物的本質。

這種情況,我們有如下幾種方法解決:

  • Copy & Paste 這是一個糟糕的解決方案,我們應該儘量避免這種做法。
  • BaseViewController 在一個繼承自 UIViewControllerBaseViewController上添加需要共享的代碼,或者在 UIViewController上添加 extension。這是目前很多人常用的解決方法,但是如果不斷這麼做,會讓所謂的BaseViewController很快變成垃圾堆。職責不明確,任何東西都能扔進 Base,你完全不知道哪些類走了 Base,而這個“超級類”對代碼的影響也會不可預估。
  • 依賴注入 通過外界傳入一個帶有 customMethod的對象,用新的類型來提供這個功能。這是一個稍好的方式,但是引入額外的依賴關係,可能也是我們不太願意看到的。
  • 多繼承 當然,Swift 是不支持多繼承的。不過如果有多繼承的話,我們確實可以從多個父類進行繼承,並將customMethod添加到合適的地方,但這又會帶來其他問題。

總的來説,面向協議編程(POP) 帶來的好處如下:

  • 結構體、枚舉等值類型也可以使用
  • 以繼承多個協議,彌補 swift 中類單繼承的不足
  • 增強代碼的可擴展性,減少代碼的宂餘
  • 讓項目更加組件化,代碼可讀性更高
  • 讓無需的功能代碼組成一個功能塊,更便於單元測試。


三、使用POP解決上述問題


定義一個含有customMethod的協議ex:

protocol ex {
    func customMethod();
}

在實際類型遵守這個協議:

extension ViewController :ex {
    func customMethod() {
        //
    }
}
extension OtherViewController : ex {
    func customMethod() {
        //
    }
}

這樣的實現和Copy & Paste的方式一樣,必須要在ViewControllerOtherViewController都寫一遍。有什麼方法可以解決這問題呢?那就是協議擴展

可以在 extension ex 中為 customMethod 添加一個實現:

extension ex {
    func customMethod() {
        //
    }
}

有這個協議擴展,只需要聲明ViewControllerOtherViewController遵循ex,就可以直接使用customMethod了。

四、協議的特性及使用


協議擴展:
  • 1.提供協議方法的默認實現和協議屬性的默認值,從而使它們成為可選;符合協議的類型可以提供自己的實現,也可以使用默認的實現。
  • 2.添加協議中未聲明的附加方法實現,並且實現協議的任何類型都可以使用到這些附加方法。這樣就可以給遵循協議的類型添加特定的方法

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 InMemoryEntityEntity
 {
    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")


 推薦↓↓↓ 

👉16個技術公眾號】都在這裏!

涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、數據庫研發、幽默程序員等。

萬水千山總是情,點個 “在看” 行不行
https://hk.wxwenku.com/d/201348637