Swift - 面向協議編程(POP)

1、OOP與POP

面向對象程序設計(Object Oriented Programming) 其本質是以創建模型體現出來的抽象思惟過程和麪向對象的方法。模型是用來反映現實世界中事物特徵的。任何一個模型都不可能反映客觀事物的一切具體特徵,只能對事物特徵和變化規律的一種抽象,且在它所涉及的範圍內更廣泛、更集中、更深入地描述客體的特徵。經過創建模型而達到的抽象是人們對客體認識的深化python

正是由於化零爲整的功效,方便儲存數據而後傳輸! 面向對象的設計,在衆多語言裏面被採用!OC - Swift 主流方式也是 OOP. 這個相比你們已經很是熟悉了,這裏也再也不囉嗦。今天的主角是 POP (Protocol Oriented Programming) 一 面向協議編程面試

面向對象的困境編程

王巍面向協議編程 這一篇文章裏面就有詳細表達!json

  • 橫切關注點
  • 動態派發安全性
  • 菱形缺陷

2、POP 面向協議編程

面向協議編程的思惟,在 Swift開發中很是很是重要!能夠說若是你用好了 POP 那麼你的項目絕對逼格不是一個level 下面咱們經過解決 OOP 問題的思路展開分析api

POP 解決橫切關注點安全

橫切關注點(Cross-Cutting Concerns) 那就是咱們很難在不一樣繼承關係的類裏共用代碼! 如今咱們經過面向協議的方式,任何遵循協議的,均可以默認添加 name屬性以及sayHello()方法!bash

protocol LGProtocl {
    /// 協議屬性
    var name: String {get}
    /// 協議方法
    func sayHello()
}
複製代碼
  • 可是這裏還有一個問題:缺少實現!若是這樣提供聲明,我還須要在每個類裏面實現,有不少時候其實這些方法都是共有,不須要太多特定實現
  • 幸虧在 WWDC 2015Swift 2 發佈時,Apple 爲協議引入了一個新特性一 協議擴展 它爲 Swift 語言帶來了一次革命性的變化。
struct LGTeacher: LGProtocl{
    var name: String
    func sayHello() {
        print("你好")
    }
}
複製代碼
  • 經過協議定義,提供實現的入口,遵循協議的類型須要對其進行實現
  • 協議擴展,爲入口提供默認實現。根據入口提供額外實現

這樣的操做有什麼做用了? 一 萬物皆 lg網絡

// 聲明協議
extension LGCompatible {
    /// Reactive extensions.
    public static var lg: LGRxSwift.Reactive<Self>.Type
    /// Reactive extensions.
    public var lg: LGRxSwift.Reactive<Self>
}
// NSObject 實現
extension NSObject : LGCompatible { }
複製代碼
  • 這樣完美實現了萬物皆lg的特性
  • 而後經過 LGCompatible 引伸到Reactive 響應式類或者結構體
  • 最後經過不斷拓展 Reactive 的能力,就能完美切合

POP 解決動態派發安全性app

對於沒有實現 LGProtocl 的類型,編譯器將返回錯誤,所以不存在消息誤發送的狀況框架

// Compiler Error: 
// 'LGTeacher' does not conform to protocol 'LGProtocl'
// protocol requires function 'sayHello()'
複製代碼

POP 解決菱形缺陷

最後咱們看看多繼承。多繼承中存在的一個重要問題是菱形缺陷,也就是子類沒法肯定使用哪一個父類的方法。在協議的對應方面,這個問題雖然依然存在,但倒是能夠惟一安全地肯定的。

這裏很遺憾POP在 解決菱形缺陷 這一點上也存在一樣的BUG , 由於多個協議存在相同的協議屬性、協議方法,遵循者也是沒法肯定的!⚠️ 咱們在平時開發中必定要儘可能規避同名協議遵循問題,咱們在模塊劃分上面必定要作作到完全,儘管 Swift 還不能很好地處理多個協議的衝突 可是咱們能夠在協議層功能抽層嚴格機智處理上浮與下沉功能。

舉個例子:🌰

  • 咱們剛剛是否是作了LGCompatible, 它是咱們功能的入口,萬物皆lg,進來,那麼這層協議只須要提供入口就完畢

  • 同時提供接口過分能力,把 LGReactiveCompatiblelg 過分到 Reactive

extension LGReactiveCompatible {
    /// Reactive extensions.
    public static var lg: Reactive<Self>.Type {
        get { return Reactive<Self>.self }
        set {// this enables using Reactive to "mutate" base type }
    }
    /// Reactive extensions.
    public var lg: Reactive<Self> {
        get { return Reactive(self) }
        set { // this enables using Reactive to "mutate" base object }
    }
}
複製代碼
  • Reactive層, 根據業務劃分開來,達到邏輯代碼下沉效果!
public struct Reactive<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}
複製代碼
  • 根據 Reactive 裏面關聯的 Base 類型來肯定不一樣的響應式功能
  • 好比:extension Reactive where Base: UISwitch 其中 UISwitch 能夠換成 UITableView、UITextField、UIView ... 不斷業務下沉!

相信到這裏你已經感覺到了面向協議編程的方便之處,可是還有一個很是重要的特性沒有展示出來就是 一 耦合度大大下降,代碼分層,邏輯更清晰

3、POP 網絡

咱們在實際開發中,網絡請求是一個很是重要的模塊

Alamofire.request("http://127.0.0.1:5000/pythonJson/")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseData { response in
        switch response.result {
        case .success:
            print(response)
            let _ = LGLoginClient.lgJson(data: response.data)
        case .failure(let error):
            print(error)
        }
}
複製代碼
  • 上面這段代碼,沒有錯!可是若是你是一個資深iOS開發,確定會發現問題!
  • 若是你直接在 ViewController (表明應用層) 直接這麼網絡請求,耦合度是很是大的(應用層與網絡層耦合在一塊兒)
  • 還有處處嵌套,可複用性特別低
  • 應用層其實根本不該該關心網絡請求的method、接口、參數 說白了我也不想關心
  • 若是系統模塊化處理,那我就很是 happy 😄😄😄

1️⃣:網絡信息能力

enum LGHTTPMethod: String {
    case GET
    case POST
}

protocol LGRequest {
    var host: String { get }
    var path: String { get }
    var method: LGHTTPMethod { get }
    var parameter: [String: Any] { get }
    
    associatedtype Response
    func parse(data: Data) -> Response?
}
複製代碼
  • LGHTTPMethod 提供本模塊LGRequest 須要的請求方法枚舉
  • LGRequest 是登陸註冊模塊的請求能力賦予者,經過面向協議的方式給咱們的模塊提供能力
  • Response 這個關聯類型,方便後面 json 轉模型,設置這個泛型類型是可以通用化

2️⃣:模塊信息層

struct LGLoginRequest: LGRequest {
    typealias Response = LGPerson
    let name: String
    
    let host = "http://127.0.0.1:5000"
    var path: String {
        return "/pythonJson/getTeacherInfo/?username=\(name)"
    }
    let method: LGHTTPMethod = .GET
    let parameter: [String: Any] = [:]
    
    func parse(data: Data) -> LGPerson? {
        return LGPerson(data: data)
    }
}
複製代碼
  • LGLoginRequest 遵循 LGRequest 得到host、path、method、parameter、parse 處理能力,在這裏能夠直接處理,就不須要到應用層再去傳值!

3️⃣:網絡請求能力

extension LGRequest {
    func send(handler: @escaping (Response?) -> Void) {
        let url = URL(string: host.appending(path))!
        var request = URLRequest(url: url)
        request.httpMethod = method.rawValue
        let task = URLSession.shared.dataTask(with: request) {
            data, response, error in
            if let data = data, let res = self.parse(data: data) {
                DispatchQueue.main.async { handler(res) }
            } else {
                DispatchQueue.main.async { handler(nil) }
            }
        }
        task.resume()
    }
}
複製代碼
  • 由於面向協議,咱們利用協議提供公用網絡請求能力
  • 其中 Response 就是相應模型的具體類型

4️⃣:應用層調用

override func viewDidLoad() {
    super.viewDidLoad()
    let request = LGPersonRequest(name: "Cooci")
    request.send { (person) in
        self.updataUI(person: person!)
    }
}
複製代碼
  • 應用層與網絡層徹底分隔開來
  • 應用層只提供必要的參數信息,具體調用哪一個接口,怎麼處理交給下層
  • LGPersonRequest 模塊信息處理提供層
  • LGPersonRequest 同時還具有調用網絡能力

5️⃣:POP網絡優化重構

很顯然咱們的 LGRequest 這個傢伙的能力太強了!能提供信息,還能發起請求,連序列化的處理能力也是由LGRequest提供!優化重構迫在眉睫。。。。

1:信息提供能力者

protocol LGRequest {
    var path: String { get }
    var method: LGHTTPMethod { get }
    var parameter: [String: Any] { get }
    
    associatedtype Response: LGDecodable
}

struct LGPersonRequest: LGRequest {
    typealias Response = LGPerson
    
    let name: String
    var path: String {
        return "/pythonJson/getTeacherInfo/?username=\(name)"
    }
    let method: LGHTTPMethod = .GET
    let parameter: [String: Any] = [:]
}
複製代碼
  • 把公共提供的 host 提取出去
  • 再也不提供 LGRequest 網絡請求能力
  • 序列化交付給具體的模型,提供一個序列化能力 LGDecodable
protocol ClientProtocol {
    var host: String { get }
    func send<T: LGRequest>(_ r: T, handler: @escaping (T.Response?) -> Void)
}


class LGClient: ClientProtocol{
    static let manager = LGClient()
    let host: String = "http://127.0.0.1:5000"
    
    func send<T>(_ r: T, handler: @escaping (T.Response?) -> Void) where T : LGRequest {
        
        let url = URL(string: host.appending(r.path))!
        var request = URLRequest(url: url)
        request.httpMethod = r.method.rawValue
        let task = URLSession.shared.dataTask(with: request) {
            data, response, error in
            if let data = data, let res = T.Response.parse(data: data) {
                DispatchQueue.main.async { handler(res) }
            } else {
                DispatchQueue.main.async { handler(nil) }
            }
        }
        task.resume()
    }
}
複製代碼
  • 提供一個網絡管理類 LGClient
  • 管理網絡請求能力和公共提供參數

2:網絡能力提供者

class LGLoginClient: LGClient {
    
    override func send<T>(_ r: T, handler: @escaping (T.Response?) -> Void) where T : LGRequest {
        
        switch r.path {
        case let string where string.contains("/pythonJson/getTeacherInfo"):
            print("123456")
            handler(nil)
        default:
            let url = URL(string: host.appending(r.path))!
            var request = URLRequest(url: url)
            request.httpMethod = r.method.rawValue
            let task = URLSession.shared.dataTask(with: request) {
                data, response, error in
                if let data = data, let res = T.Response.parse(data: data) {
                    DispatchQueue.main.async { handler(res) }
                } else {
                    DispatchQueue.main.async { handler(nil) }
                }
            }
            task.resume()
        }

    }
}
複製代碼
  • 模塊網路能力層LGLoginClient重寫,根據不一樣的接口分塊處理
  • 其中序列化層交給泛型模型 (T.Response) 處理
  • 固然你們這裏還能夠將網絡能力繼續下沉,就給具體網絡綜合請求者去處理

3:序列化能力提供層

extension LGPerson: LGDecodable {
    static func parse(data: Data) -> LGPerson? {
        return LGPerson(data: data)
    }
}
複製代碼
  • 這裏序列化就簡單表達了,你們能夠調用一些優化的三方框架

6️⃣:小結

  • POP網絡,讓應用層與網絡層徹底脫離!
  • 面向協議的編程方式來提供能力,大大拓展了複用性,同時耦合度也得以處理!
  • 同時這個模型(應用層 -> 信息提供層 -> 網絡層) 編程思惟也是比較容易理解,操做更容易上手!
  • 還有這樣的編程習慣也符合開發流程(通常都是由後臺開發人員作出相應接口,咱們纔會去作網絡調試)
  • 後期維護簡單,後期更改,咱們只須要對信息提供層處理響應配置,根本不須要去動應用層或者網絡層
  • 固然這也是主流開發思惟,做爲一名中高級iOS開發人員不動 POP網絡編程 那麼我估計你須要好好學習咯!💪💪💪

最近有從朋友那裏拿到一些iOS面試題總結的pdf文檔!由於平臺不能發送文件,須要的朋友能夠添加QQ羣:679884541,羣裏面都是iOS開發者,有什麼問題和牢騷你們一塊兒交流吧!

相關文章
相關標籤/搜索