接口編程那些事

轉載請注明出處:http://www.olinone.com/

接口是一系列可調用方法的集合。何為接口編程录豺?接口編程是指當寫一個函數或一個方法時唠亚,我們應該更加關注具體的接口,而不是實現類峡谊。具體理解可以參考這篇文章

在OC中,接口又可以理解為Protocol刊苍,面向接口編程又可以理解為面向Protocol編程,或者面向協(xié)議編程濒析。在Swift中正什,蘋果大幅強化了 Protocol 在這門語言中的地位,整個 Swift 標準庫也是基于 Protocol 來設計的号杏,有興趣的童鞋可以看看這篇文章婴氮。面向接口編程正逐步成為程序開發(fā)的主流思想

在實際開發(fā)中,大多數朋友都比較熟悉對象編程盾致,比如主经,使用ASIHttpRequest執(zhí)行網絡請求

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

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

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

[request startAsynchronous];

request是請求對象,當發(fā)起請求時庭惜,調用者需要知道給對象賦哪些屬性或者調用對象哪些方法罩驻。然而,使用AFNetworking請求方式卻不盡相同

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發(fā)起請求時,調用者可以不需要關心它有哪些屬性节吮,只有接口無法滿足需求時才需要了解相關屬性的定義抽高。兩種設計思路完全不同,當然透绩,此處并不是想表明孰優(yōu)孰劣翘骂,只是想通過兩種截然不同的請求方式引出本文的思想——面向接口編程(或者面向協(xié)議編程)

接口比屬性直觀

在對象編程中,定義一個對象時帚豪,往往需要為其定義各種屬性雏胃。比如,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

通過接口的定義瞭亮,調用者可以忽略對象的屬性,聚焦于其提供的接口和功能上固棚。程序猿在首次接觸陌生的某個對象時统翩,接口往往比屬性更加直觀明了,抽象接口往往比定義屬性更能描述想做的事情

接口依賴

設計一個APIService對象

@interface ApiService : NSObject

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

@property (nonatomic, strong) NSDictionary *param;

- (void)execNetRequest;

@end

正常發(fā)起Service請求時此洲,調用者需要直接依賴該對象厂汗,起不到解耦的目的。當業(yè)務變動需要重構該對象時呜师,所有引用該對象的地方都需要改動娶桦。如何做到既能滿足業(yè)務又能兼容變化?抽象接口也許是一個不錯的選擇汁汗,以接口依賴的方式取代對象依賴衷畦,改造代碼如下

@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對象知牌,也無需了解其有哪些屬性祈争。即使需要重構替換新的對象,調用邏輯也不受任何影響角寸。調用接口往往比訪問對象屬性更加穩(wěn)定可靠

抽象對象

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

參考Swift抽象協(xié)議的設計理念亿柑,可以使用Protocol抽象對象邢疙,畢竟調用者也不關心具體實現類。Protocol可以定義方法,可是屬性的問題怎么解決秘症?此時照卦,裝飾器模式也許正好可以解決該問題,讓我們試著繼續(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

經過Protocol的改造乡摹,ApiService對象化身為ApiService接口役耕,其不再依賴于任何對象,做到了真正的接口依賴取代對象依賴聪廉,具有更強的業(yè)務兼容性

定義一個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,并且可以繼承多個Protocol板熊,Protocol具有更強的靈活性框全。某一天,業(yè)務需求變更需要用到新的Post請求時干签,可以不用修改 GetApiService一行代碼津辩,定義一個新的 PostApiService實現Post請求即可,避免了對象里面出現過多的if-else代碼容劳,也保證了代碼的整潔性

依賴注入

文章寫到這里喘沿,細心的童鞋可能已經發(fā)現問題——GetApiService依然是以對象依賴的形式存在。如何解決這個問題竭贩?沒錯蚜印,那就是依賴注入!

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

基于依賴注入objection開源庫的基礎上繼續(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

調用者關心請求接口青柄,實現者關心需要實現的接口,各司其職预侯,互不干涉

接口和實現分離的設計適用于團隊協(xié)作開發(fā),實現了系統(tǒng)的松散耦合,便于以后升級擴展回还。當然抛人,接口編程對開發(fā)人員的要求也比較高,需要提前定義好接口糜芳,接口一變飒货,全部亂套魄衅,這就是所謂的設計比實現難,但是設計接口的人工資都高疤粮ā;纬妗!扣墩!


后記:你的支持是我前進的最大動力哲银,你可以在github找到我,也可以通過微博聯系我呻惕,感謝你的來訪荆责!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市亚脆,隨后出現的幾起案子做院,更是在濱河造成了極大的恐慌,老刑警劉巖濒持,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件键耕,死亡現場離奇詭異,居然都是意外死亡弥喉,警方通過查閱死者的電腦和手機郁竟,發(fā)現死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來由境,“玉大人棚亩,你說我怎么就攤上這事÷步埽” “怎么了讥蟆?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纺阔。 經常有香客問我瘸彤,道長,這世上最難降的妖魔是什么笛钝? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任质况,我火速辦了婚禮,結果婚禮上玻靡,老公的妹妹穿的比我還像新娘结榄。我一直安慰自己,他們只是感情好囤捻,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布臼朗。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪视哑。 梳的紋絲不亂的頭發(fā)上绣否,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音挡毅,去河邊找鬼蒜撮。 笑死,一個胖子當著我的面吹牛慷嗜,可吹牛的內容都是我干的淀弹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼庆械,長吁一口氣:“原來是場噩夢啊……” “哼薇溃!你這毒婦竟也來了?” 一聲冷哼從身側響起缭乘,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤沐序,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后堕绩,有當地人在樹林里發(fā)現了一具尸體策幼,經...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年奴紧,在試婚紗的時候發(fā)現自己被綠了特姐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡黍氮,死狀恐怖唐含,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情沫浆,我是刑警寧澤捷枯,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站专执,受9級特大地震影響淮捆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜本股,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一攀痊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拄显,春花似錦蚕苇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盒件,卻和暖如春蹬碧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背炒刁。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工恩沽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翔始。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓罗心,卻偏偏與公主長得像,于是被迫代替她去往敵國和親城瞎。 傳聞我的和親對象是個殘疾皇子渤闷,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內容