編程範式 --- 面向協議編程(Protocol Oriented Programming,簡稱POP)

  • 面向協議編程(Protocol Oriented Programming,簡稱POP)
  • 是Swift的一種編程範式,Apple於2015年WWDC踢出
  • 在Swift的標準庫中,能見到大量POP的影子
  • 同時,Swift也是一門面向對象的編程語言(Objec Oriented Programming,簡稱OOP)
  • 在Swift開發中,OOP和POP是相輔相成的,任何一方並不能取代另外一方

P- OP能彌補OOP一些設計上的不足編程

回顧OOP

  • OOP的三大特性:封裝、繼承、多態
  • 繼承的經典使用場合:
  • 當多個類(好比A、B、C類)具備不少共性時,能夠將這些共性抽取到一個父類中(好比D類),最後A、B、C類繼承D類
    在這裏插入圖片描述

OOP的不足

  • 可是有些問題,使用OOP並不能很好解決,好比:
  • 如何將BVC、DVC的公共方法run抽取出來?
class BVC: UIViewController {
    func run() {
        print("run")
    }
}複製代碼
class DVC: UITableViewController {
    func run() {
        print("run")
    }
}複製代碼

在這裏插入圖片描述

  • 基於OOP想到的一些解決方案?
  1. 將run方法翻到另外一個對象A中,而後BVC、DVC擁有對象A屬性(多了一些額外的依賴關係)
  2. 將run方法添加到UIViewController分類中(UIViewController會愈來愈臃腫,並且會影響它其餘的全部子類)
  3. 將run方法抽取到薪的父類,採用多繼承?(C++支持多繼承)(會增長程序設計複雜度,產生菱形繼承等問題,須要開發者額外解決)

POP的解決方案

protocol Runnable {
    func run()
}
extension Runnable {
    func run() {
        print("run")
    }
}複製代碼
class BVC: UIViewController, Runnable {}
class DVC: UIViewController, Runnable {}複製代碼

再舉例

在這裏插入圖片描述

POP的注意點

  • 優先考慮建立協議,而不是父類(基類)
  • 優先考慮值類型(struct、enum),而不是引用類型(class)
  • 巧用協議的擴展功能
  • 不要爲了面向協議而使用協議
    --------------------------------------------------------------------------------

利用協議實現前綴效果

struct My {
    var string: String
    init(_ string: String) {
        self.string = string
    }
    var numberCount: Int {
        var count = 0
        for c in string where("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}
extension String {
    var my: My { return My(self) }
}
print("123kkk".my.numberCount) // 3複製代碼
  • 泛型優化 - 不用添加n多個不一樣類型的存儲屬性
struct My<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}複製代碼
  • 擴展優化 - 給具體類型擴展實例方法和實例屬性
extension My where Base == String { 
    var numberCount: Int {
        var count = 0
        for c in base where("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}複製代碼
  • 擴展類型方法和屬性
class Person {}
extension Person {
    var my: My<Person> { My(self) }
    static var my: My<Person>.Type { My(Person).self }
}
extension My where Base == Person { // 須要給對應子類擴充的話使用冒號,例如:Base: Person
    func run() {
        print("run")
    }
    static func test() {
        print("test")
    }
}
Person().my.run()
Person.my.test()複製代碼
  • 協議優化 - 不用給每個類型擴展前綴屬性
protocol MyCompatible {}
extension MyCompatible {
    var my: My<Self> { My(self) }
    static my: My<Self>.Type { My<Self>.self }
}
class String {}
extension String: MyCompatible {}複製代碼
  • 最終版本
/// 前綴類型
struct My<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base;
    }
}
/// 利用協議擴展前綴屬性
protocol MyCompatible {}
extension MyCompatible { 
    var my: My<Self> { 
        set {}    // *** 爲了擴充mutating方法,因此不能寫只讀屬性
        get { My(self) }
     }
    static var my: My<Self>.Type { 
        set {}    // *** 爲了擴充mutating方法,因此不能寫只讀屬性
        get { My<Self>.self }
     }
}
/// 給字符串擴展功能
// 讓String擁有my前綴屬性
extension String : MyCompatible {}
// 給String前綴擴展功能
extension My where Base == String {
    var numberCount: Int {
        var count = 0
        for c in base where("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    mutating func run() {}
    static func test() {}
}
var str = "666"
str.my.run()
String.my.test()複製代碼

Base的補充

  • 擴展能夠擴充子類class
class Person {}
class Student: Person {}
extension Person: MyCompatible {}
extension My where Base: Person {
    func run() {}
    static func test() {}
}
Person.my.test()
Student.my.test()

let p = Person()
p.my.run()

let s = Student()
s.my.run()複製代碼
  • 擴展能夠遵照共同協議
  • 同時給String、NSString和NSMutableString擴展功能
extension String: MyCompatible {}
extension NSString: MyCompatible {}
extension My where Base: ExpressibleByStringLiteral {
    var numberCount: Int {
        var count = 0
        for c in (base as! String) where("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    mutating func run() {}
    static func test() {}
}
var str1 = "123xxx"
var str2: NSString = "123xxx"
var str3: NSMutableString = "123xxx"
print(str1.my.numbserCount)
print(str2.my.numbserCount)
print(str3.my.numbserCount)複製代碼

利用協議實現類型判斷

func isArray(_ value: Any) -> Bool {
    // value is Array<Any>
    value is [Any]
}
print(isArray([1, 2]))            // true
print(isArray(["1", 2]))          // true
print(isArray(NSArray()))         // true 
print(isArray(NSMutableArray()))  // true
print(isArray("12312421"))        // false複製代碼
protocol ArrayType {}
extension Array: ArrayType {}
extension NSArray: ArrayType {}
func isArrayType(_ type: AnyType) -> Bool { type is ArrayType.Type }
isArrayType([Int].self)
isArrayType([Any].self)
isArrayType(NSArray.self)
isArrayType(NSMuatbleArray.self)複製代碼
相關文章
相關標籤/搜索