Swift Day 21 面向協(xié)議編程 POP (重要)

一、對 POP 的基礎(chǔ)認(rèn)知

1. 什么是面向協(xié)議編程?會取代面向?qū)ο缶幊虇幔?/h5>
  • 面向協(xié)議編程(Protocol Oriented Programming),簡稱 POP。是 Swift 的一種編程范式 ,Apple 于 2015 年在 WWDC 提出,在 Swift 的標(biāo)準(zhǔn)庫中能見到大量 POP 的影子。
  • 同時,Swift 也是一門面向?qū)ο蟮木幊陶Z言(Object Oriented Programming,簡稱 OOP)。在 Swift 開發(fā)中,OOP 和 POP 是相輔相成的,任何一方并不能取代另一方。
  • POP 的出現(xiàn)能彌補 OOP 一些設(shè)計上的不足。
2. 回顧 OOP 的三大特性是什么?并說一下繼承的經(jīng)典使用場合?
  • OOP 的三大特性:繼承、封裝、多態(tài)
  • 繼承的經(jīng)典使用場合:當(dāng)多個類(比如 A、B、C 類)具有很多共性時,可以將這些共性抽取到一個父類中(比如 D 類),最后 A、B、C類繼承 D 類
3. 傳統(tǒng) OOP 面臨的問題?以及它們的解決方案? (重要)
image.png
4. 如何解決上述 OOP 無法很好解決的問題?
  • Swift 中可以擴展一個協(xié)議的具體實現(xiàn),很好的解決了上述問題。
    image.png
5. POP 的注意點?(說兩點即可)
  • 優(yōu)先考慮創(chuàng)建協(xié)議,而不是父類(基類)
  • 優(yōu)先考慮值類型(struct、enum),而不是引用類型(class)
  • 巧用協(xié)議的擴展功能
  • POP 和 OOP 是相輔相成的,不要強行 POP

二、優(yōu)雅的前綴

1. 優(yōu)雅前綴版本一:如何給系統(tǒng)類(String)擴展自定義(numberCount)方法?
extension String {
    func numberCount() -> Int {
        self.reduce(0) {("0"..."9").contains($1) ? $0 + 1 : $0}
    }
}

print("122abcde10294".numberCount()) // 輸出:8
  • 上面的辦法雖然實現(xiàn)了numberCount方法擴展,但是如何保證不和系統(tǒng)的辦法沖突呢?如果有一天 Apple 也給 String 添加了 numberCount方法且含義不同怎么辦?
2. 優(yōu)雅前綴版本二:不和系統(tǒng)沖突
  • 從 OC 的經(jīng)驗來看,我們可以給numberCount添加前綴,這樣就能保證不和別人沖突了。實現(xiàn)如下:
extension String {
    var mj_numberCount: Int {
        self.reduce(0) {("0"..."9").contains($1) ? $0 + 1 : $0}
    }
}

print("122abcde10294".mj_numberCount) // 輸出:8
  • 但是上面做法不夠 Swift 風(fēng)格,我們希望如下調(diào)用該方法
var str = "122abcde10294"
print(str.mj.numberCount) 
  • 具體實現(xiàn)如下:給 String 擴展一個 mj 計算屬性
struct MJ {
    var str = ""
    init(_ str: String) {
        self.str = str
    }
    var numberCount: Int {
        str.reduce(0) {("0"..."9").contains($1) ? $0 + 1 : $0}
    }
}
extension String {
    var mj: MJ {
        MJ(self)
    }
}
var str = "122abcde10294"
print(str.mj.numberCount) // 輸出:8
  • 上面代碼依然存儲問題,①如果我們需要給 Array 也擴展 mj 呢?②如果我們需要給 Array 也擴展 numberCount 方法呢?③如果我們需要給 String 擴展 characterCount 方法呢?
  • 上面的代碼就將造成我們無法很好解決上述問題,也無法有很好的擴展性
3. 優(yōu)雅前綴版本三:擴展類方法、使用泛型
struct MJ<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

struct Person {
    var age: Int
    var name: String
}


//給 String 擴展
extension String {
    var mj: MJ<String> {MJ(self)}
    static var mj: MJ<String>.Type {MJ.self}
}
extension MJ where Base == String {
    var numberCount: Int {
        base.reduce(0) {("0"..."9").contains($1) ? $0 + 1 : $0}
    }
    static func sayHello() { print("Hello String: ", Base.self)}
}
//給 Person 擴展
extension Person {
    var mj: MJ<Person> {MJ(self)}
    static var mj: MJ<Person>.Type {MJ.self}
}
extension MJ where Base == Person {
    var numberCount: Int {
        return base.age
    }
    static func sayHello() { print("Hello Person: ", Base.self)}
}

var str = "122abcde10294"
print(str.mj.numberCount)                   // 輸出:8
String.mj.sayHello()                        // 輸出:Hello String:  String
var person = Person(age: 20, name: "carrot")
print(person.mj.numberCount)                // 輸出:20
Person.mj.sayHello()                        // 輸出:Hello Person:  Person
  • 上述代碼就解決了給不同 類型擴展 mj 的需求,也能很好的擴展各種方法。
  • 但是依然存在問題① var mj: MJ<Person> {MJ(self)}; static var mj: MJ<Person>.Type {MJ.self} 這部分代碼是重復(fù)的代碼,可有辦法解決?②如果要擴展 mutating 修飾的方法可以嗎?
4. 優(yōu)雅前綴版本四: 使用協(xié)議(最終版)
// 定義基礎(chǔ)類,也是擴展前綴
struct MJ<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

protocol MJCompatible {}
// 在這里擴充 MJCompatible 的方法實現(xiàn)
extension MJCompatible{
    static var mj: MJ<Self>.Type {
        get {MJ<Self>.self}
        set {} // 加 set 適配 mutating 方法
    }
    var mj: MJ<Self> {
        get {MJ(self)}
        set {}
    }
}

struct Person {
    var age: Int
    var name: String
}


//給 String 擴展
extension String: MJCompatible {}
extension MJ where Base == String {
    var numberCount: Int {
        base.reduce(0) {("0"..."9").contains($1) ? $0 + 1 : $0}
    }
    static func sayHello() { print("Hello String: ", Base.self)}
}
//給 Person 擴展
extension Person: MJCompatible {}
extension MJ where Base == Person {
    var numberCount: Int {
        return base.age
    }
    static func sayHello() { print("Hello Person: ", Base.self)}
    mutating func modifyAge(age: Int) {
        base.age = age
        print("enter modifyAge", age, base.age)
    }
}

var str = "122abcde10294"
print(str.mj.numberCount)                   // 輸出:8
String.mj.sayHello()                        // 輸出:Hello String:  String
var person = Person(age: 20, name: "carrot")
print(person.mj.numberCount)                // 輸出:20
person.mj.modifyAge(age: 10)
// (下面沒有輸出 10,因為MJ(self)值傳遞,如果把 Person 從 struct 改成 class,就會輸出 10)
print(person.age)                           // 輸出:20
Person.mj.sayHello()                        // 輸出:Hello Person:  Person
5. 利用協(xié)議實現(xiàn)類型判斷(了解)
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容