面向協議程式設計
介面是一系列可呼叫方法的集合。何為介面程式設計?介面程式設計是指當寫一個函式或一個方法時,我們應該更加關注具體的介面,而不是實現類。具體理解可以參考這篇文章
在OC中,介面又可以理解為Protocol,面向介面程式設計又可以理解為面向Protocol程式設計,或者面向協議程式設計。在Swift中,蘋果大幅強化了 Protocol 在這門語言中的地位,整個 Swift 標準庫也是基於 Protocol 來設計的,有興趣的童鞋可以看看這篇文章。面向介面程式設計正逐步成為程式開發的主流思想
在實際開發中,大多數朋友都比較熟悉物件程式設計,比如,使用ASIHttpRequest執行網路請求
1 2 3 4 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setDidFinishSelector:@selector(requestDone:)]; [request setDidFailSelector:@selector(requestWrong:)]; [request startAsynchronous]; |
request是請求物件,當發起請求時,呼叫者需要知道給物件賦哪些屬性或者呼叫物件哪些方法。然而,使用AFNetworking請求方式卻不盡相同
1 2 3 4 5 6 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; [manager GET:@ "www.olinone.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@ "好網站,贊一個!" ); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { //to do }]; |
同是請求物件,使用AFNetworking發起請求時,呼叫者可以不需要關心它有哪些屬性,只有介面無法滿足需求時才需要了解相關屬性的定義。兩種設計思路完全不同,當然,此處並不是想表明孰優孰劣,只是想通過兩種截然不同的請求方式引出本文的思想——面向介面程式設計(或者面向協議程式設計)
介面比屬性直觀
在物件程式設計中,定義一個物件時,往往需要為其定義各種屬性。比如,ReactiveCocoa中RACSubscriber物件定義如下
1 2 3 4 5 6 7 | @interface RACSubscriber () @property (nonatomic, copy) void (^next)(id value); @property (nonatomic, copy) void (^error)(NSError *error); @property (nonatomic, copy) void (^completed)(void); @end |
參考AFNetworking的思想,以介面的形式提供訪問入口
1 2 3 4 5 6 7 | @interface RACSubscriber + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed; @end |
通過介面的定義,呼叫者可以忽略物件的屬性,聚焦於其提供的介面和功能上。程式猿在首次接觸陌生的某個物件時,介面往往比屬性更加直觀明瞭,抽象介面往往比定義屬性更能描述想做的事情
介面依賴
設計一個APIService物件
1 2 3 4 5 6 7 8 | @interface ApiService : NSObject @property (nonatomic, strong) NSURL *url; @property (nonatomic, strong) NSDictionary *param; - (void)execNetRequest; @end |
正常發起Service請求時,呼叫者需要直接依賴該物件,起不到解耦的目的。當業務變動需要重構該物件時,所有引用該物件的地方都需要改動。如何做到既能滿足業務又能相容變化?抽象介面也許是一個不錯的選擇,以介面依賴的方式取代物件依賴,改造程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @protocol ApiServiceProtocol - (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param; @end @interface NSObject (ApiServiceProtocol) @end @implementation NSObject (ApiServiceProtocol) - (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param { ApiService *apiSrevice = [ApiService new ]; apiSrevice.url = url; apiSrevice.param = param; [apiSrevice execNetRequest]; } @end |
通過介面的定義,呼叫者可以不再關心ApiService物件,也無需瞭解其有哪些屬性。即使需要重構替換新的物件,呼叫邏輯也不受任何影響。呼叫介面往往比訪問物件屬性更加穩定可靠
抽象物件
定義ApiServiceProtocol可以隱藏ApiService物件,但是受限於ApiService物件的存在,業務需求發生變化時,仍然需要修改ApiService邏輯程式碼。如何實現在不修改已有ApiService業務程式碼的條件下滿足新的業務需求?
參考Swift抽象協議的設計理念,可以使用Protocol抽象物件,畢竟呼叫者也不關心具體實現類。Protocol可以定義方法,可是屬性的問題怎麼解決?此時,裝飾器模式也許正好可以解決該問題,讓我們試著繼續改造ApiService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @protocol ApiService // private functions @end @interface ApiServicePassthrough : NSObject @property (nonatomic, strong) NSURL *url; @property (nonatomic, strong) NSDictionary *param; - (instancetype)initWithApiService:(id)apiService; - (void)execNetRequest; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @interface ApiServicePassthrough () @property (nonatomic, strong) id apiService; @end @implementation ApiServicePassthrough - (instancetype)initWithApiService:(id)apiService { if (self = [ super init]) { self.apiService = apiService; } return self; } - (void)execNetRequest { [self.apiService requestNetWithUrl:self.url Param:self.param]; } @end |
經過Protocol的改造,ApiService物件化身為ApiService介面,其不再依賴於任何物件,做到了真正的介面依賴取代物件依賴,具有更強的業務相容性
定義一個Get請求物件
1 2 3 4 5 6 7 8 9 10 | @interface GetApiService : NSObject @end @implementation GetApiService - (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param { // to do } @end |
請求程式碼也隨之改變
1 2 3 4 5 6 7 8 9 10 11 | @implementation NSObject (ApiServiceProtocol) - (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param { id apiSrevice = [GetApiService new ]; ApiServicePassthrough *apiServicePassthrough = [[ApiServicePassthrough alloc] initWithApiService:apiSrevice]; apiServicePassthrough.url = url; apiServicePassthrough.param = param; [apiServicePassthrough execNetRequest]; } @end |
物件可以繼承物件,Protocol也可以繼承Protocol,並且可以繼承多個Protocol,Protocol具有更強的靈活性。某一天,業務需求變更需要用到新的Post請求時,可以不用修改 GetApiService一行程式碼,定義一個新的 PostApiService實現Post請求即可,避免了物件裡面出現過多的if-else程式碼,也保證了程式碼的整潔性
依賴注入
文章寫到這裡,細心的童鞋可能已經發現問題——GetApiService依然是以物件依賴的形式存在。如何解決這個問題?沒錯,那就是依賴注入!
依賴注入是什麼?借用部落格裡面的一句話,其最大的特點就是:幫助我們開發出鬆散耦合、可維護、可測試的程式碼和程式。這條原則的做法是大家熟知的面向介面,或者說是面向抽象程式設計。 objc上這篇文章介紹的不錯, 有興趣的童鞋也可以看看,在此就不再累述
基於依賴注入objection開源庫的基礎上繼續改造ApiService
1 2 3 4 5 6 7 8 9 10 11 | @implementation NSObject (ApiServiceProtocol) - (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param { id apiSrevice = [[JSObjection createInjector] getObject:[GetApiService class]]; ApiServicePassthrough *apiServicePassthrough = [[ApiServicePassthrough alloc] initWithApiService:apiSrevice]; apiServicePassthrough.url = url; apiServicePassthrough.param = param; [apiServicePassthrough execNetRequest]; } @end |
呼叫者關心請求介面,實現者關心需要實現的介面,各司其職,互不干涉
介面和實現分離的設計適用於團隊協作開發,實現了系統的鬆散耦合,便於以後升級擴充套件。當然,介面程式設計對開發人員的要求也比較高,需要提前定義好介面,介面一變,全部亂套,這就是所謂的設計比實現難,但是設計介面的人工資都高啊!!!
相關文章
- 淺析面向協議程式設計協議程式設計
- Swift Protocol 詳解 - 協議&面向協議程式設計SwiftProtocol協議程式設計
- ios 面向協議程式設計資源iOS協議程式設計
- iOS - Swift 面向協議程式設計(二)iOSSwift協議程式設計
- iOS - Swift 面向協議程式設計(一)iOSSwift協議程式設計
- Swift中的面向協議程式設計Swift協議程式設計
- Swift 面向協議程式設計的那些事Swift協議程式設計
- 面向協議程式設計的一些思考協議程式設計
- iOS 工廠模式的面向協議程式設計思想iOS模式協議程式設計
- 面向協議程式設計與 Cocoa 的邂逅 (下)協議程式設計
- 面向協議程式設計與 Cocoa 的邂逅 (上)協議程式設計
- 從Swift3的標準庫協議看面向協議程式設計(一)Swift協議程式設計
- fir.im Weekly - 揭祕 iOS 面向協議程式設計iOS協議程式設計
- Swift 面向協議程式設計 基礎篇 (一) 介紹Swift協議程式設計
- Swift 中的面向協議程式設計:是否優於物件導向程式設計?Swift協議程式設計物件
- 網路程式設計UDP協議方式程式設計UDP協議
- 翻譯:Swift 5.1中的Protocol面向協議的程式設計教程:從入門到精通SwiftProtocol協議程式設計
- 程式設計思想 面向切面程式設計程式設計
- 面向介面程式設計程式設計
- Java Tcp協議socket程式設計學習JavaTCP協議程式設計
- [swift 進階]讀書筆記-第十章:協議 C10P1 面向協議程式設計 Overload Resolution for Free FunctionsSwift筆記協議程式設計Function
- MG--Swift面向協議開發Swift協議
- AOP 面向切面程式設計程式設計
- 面向架構程式設計架構程式設計
- AOP(面向切面程式設計)程式設計
- 面向切面程式設計AOP程式設計
- 面向方面的程式設計程式設計
- 面向指標程式設計指標程式設計
- Swift使用協議加泛型程式設計(一)Swift協議泛型程式設計
- 《Unix 網路程式設計》15:Unix 域協議程式設計協議
- linux下bluetooth程式設計(七)SDP協議Linux程式設計協議
- ModbusTCP協議簡介與程式設計流程圖TCP協議程式設計流程圖
- 造輪子 | 如何設計一個面向協議的 iOS 網路請求庫協議iOS
- Swift:面向協議的網路請求Swift協議
- 網路程式設計協議(TCP和UDP協議,黏包問題)以及socketserver模組程式設計協議TCPUDPServer
- 設計模式之面向切面程式設計AOP設計模式程式設計
- Android AOP面向切面設計程式設計Android程式設計
- Java 網路程式設計 – TCP協議基本步驟Java程式設計TCP協議