接口編程那些事

轉(zhuǎn)載請注明出處:http://www.olinone.com/

接口是一系列可調(diào)用方法的集合。何為接口編程?接口編程是指當(dāng)寫一個(gè)函數(shù)或一個(gè)方法時(shí),我們應(yīng)該更加關(guān)注具體的接口,而不是實(shí)現(xiàn)類。具體理解可以參考這篇文章

在OC中,接口又可以理解為Protocol,面向接口編程又可以理解為面向Protocol編程,或者面向協(xié)議編程。在Swift中,蘋果大幅強(qiáng)化了 Protocol 在這門語言中的地位,整個(gè) Swift 標(biāo)準(zhǔn)庫也是基于 Protocol 來設(shè)計(jì)的,有興趣的童鞋可以看看這篇文章。面向接口編程正逐步成為程序開發(fā)的主流思想

在實(shí)際開發(fā)中,大多數(shù)朋友都比較熟悉對象編程,比如,使用ASIHttpRequest執(zhí)行網(wǎng)絡(luò)請求

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

[request setDidFinishSelector:@selector(requestDone:)];

[request setDidFailSelector:@selector(requestWrong:)];

[request startAsynchronous];

request是請求對象,當(dāng)發(fā)起請求時(shí),調(diào)用者需要知道給對象賦哪些屬性或者調(diào)用對象哪些方法。然而,使用AFNetworking請求方式卻不盡相同

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

[manager GET:@"www.olinone.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

? ? NSLog(@"好網(wǎng)站,贊一個(gè)!");

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

? ? //to do

}];

同是請求對象,使用AFNetworking發(fā)起請求時(shí),調(diào)用者可以不需要關(guān)心它有哪些屬性,只有接口無法滿足需求時(shí)才需要了解相關(guān)屬性的定義。兩種設(shè)計(jì)思路完全不同,當(dāng)然,此處并不是想表明孰優(yōu)孰劣,只是想通過兩種截然不同的請求方式引出本文的思想——面向接口編程(或者面向協(xié)議編程)

接口比屬性直觀

在對象編程中,定義一個(gè)對象時(shí),往往需要為其定義各種屬性。比如,ReactiveCocoa中RACSubscriber對象定義如下

@interface RACSubscriber ()

@property (nonatomic, copy) void (^next)(id value);

@property (nonatomic, copy) void (^error)(NSError *error);

@property (nonatomic, copy) void (^completed)(void);

@end

參考AFNetworking的思想,以接口的形式提供訪問入口

@interface RACSubscriber

+ (instancetype)subscriberWithNext:(void (^)(id x))next

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?error:(void (^)(NSError *error))error?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?completed:(void (^)(void))completed;

@end

通過接口的定義,調(diào)用者可以忽略對象的屬性,聚焦于其提供的接口和功能上。程序猿在首次接觸陌生的某個(gè)對象時(shí),接口往往比屬性更加直觀明了,抽象接口往往比定義屬性更能描述想做的事情

接口依賴

設(shè)計(jì)一個(gè)APIService對象

@interface ApiService : NSObject

@property (nonatomic, strong) NSURL????????*url;

@property (nonatomic, strong) NSDictionary *param;

- (void)execNetRequest;

@end

正常發(fā)起Service請求時(shí),調(diào)用者需要直接依賴該對象,起不到解耦的目的。當(dāng)業(yè)務(wù)變動(dòng)需要重構(gòu)該對象時(shí),所有引用該對象的地方都需要改動(dòng)。如何做到既能滿足業(yè)務(wù)又能兼容變化?抽象接口也許是一個(gè)不錯(cuò)的選擇,以接口依賴的方式取代對象依賴,改造代碼如下

@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

通過接口的定義,調(diào)用者可以不再關(guān)心ApiService對象,也無需了解其有哪些屬性。即使需要重構(gòu)替換新的對象,調(diào)用邏輯也不受任何影響。調(diào)用接口往往比訪問對象屬性更加穩(wěn)定可靠

抽象對象

定義ApiServiceProtocol可以隱藏ApiService對象,但是受限于ApiService對象的存在,業(yè)務(wù)需求發(fā)生變化時(shí),仍然需要修改ApiService邏輯代碼。如何實(shí)現(xiàn)在不修改已有ApiService業(yè)務(wù)代碼的條件下滿足新的業(yè)務(wù)需求?

參考Swift抽象協(xié)議的設(shè)計(jì)理念,可以使用Protocol抽象對象,畢竟調(diào)用者也不關(guān)心具體實(shí)現(xiàn)類。Protocol可以定義方法,可是屬性的問題怎么解決?此時(shí),裝飾器模式也許正好可以解決該問題,讓我們試著繼續(xù)改造ApiService

@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

@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

經(jīng)過Protocol的改造,ApiService對象化身為ApiService接口,其不再依賴于任何對象,做到了真正的接口依賴取代對象依賴,具有更強(qiáng)的業(yè)務(wù)兼容性

定義一個(gè)Get請求對象

@interface GetApiService : NSObject ?

@end

@implementation GetApiService

- (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param {

? ? // to do

}

@end

請求代碼也隨之改變

@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,并且可以繼承多個(gè)Protocol,Protocol具有更強(qiáng)的靈活性。某一天,業(yè)務(wù)需求變更需要用到新的Post請求時(shí),可以不用修改 GetApiService一行代碼,定義一個(gè)新的 PostApiService實(shí)現(xiàn)Post請求即可,避免了對象里面出現(xiàn)過多的if-else代碼,也保證了代碼的整潔性

依賴注入

文章寫到這里,細(xì)心的童鞋可能已經(jīng)發(fā)現(xiàn)問題——GetApiService依然是以對象依賴的形式存在。如何解決這個(gè)問題?沒錯(cuò),那就是依賴注入!

依賴注入是什么?借用博客里面的一句話,其最大的特點(diǎn)就是:幫助我們開發(fā)出松散耦合、可維護(hù)、可測試的代碼和程序。這條原則的做法是大家熟知的面向接口,或者說是面向抽象編程。objc上這篇文章介紹的不錯(cuò), 有興趣的童鞋也可以看看,在此就不再累述

基于依賴注入objection開源庫的基礎(chǔ)上繼續(xù)改造ApiService

@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

調(diào)用者關(guān)心請求接口,實(shí)現(xiàn)者關(guān)心需要實(shí)現(xiàn)的接口,各司其職,互不干涉

接口和實(shí)現(xiàn)分離的設(shè)計(jì)適用于團(tuán)隊(duì)協(xié)作開發(fā),實(shí)現(xiàn)了系統(tǒng)的松散耦合,便于以后升級擴(kuò)展。當(dāng)然,接口編程對開發(fā)人員的要求也比較高,需要提前定義好接口,接口一變,全部亂套,這就是所謂的設(shè)計(jì)比實(shí)現(xiàn)難,但是設(shè)計(jì)接口的人工資都高?。。?!


后記:你的支持是我前進(jìn)的最大動(dòng)力,你可以在github找到我,也可以通過微博聯(lián)系我,感謝你的來訪!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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