集約型API調(diào)用其實(shí)就是所有API的調(diào)用只有一個(gè)類,然后這個(gè)類接收API名字,API參數(shù),以及回調(diào)著陸點(diǎn)(可以是target-action,或者block,或者delegate等各種模式的著陸點(diǎn))作為參數(shù)。然后執(zhí)行類似startRequest
這樣的方法,它就會(huì)去根據(jù)這些參數(shù)起飛去調(diào)用API了,然后獲得API數(shù)據(jù)之后再根據(jù)指定的著陸點(diǎn)去著陸。比如這樣:
集約型API調(diào)用方式:
[APIRequest startRequestWithApiName:@"itemList.v1" params:params success:@selector(success:) fail:@selector(fail:) target:self];
離散型API調(diào)用是這樣的,一個(gè)API對(duì)應(yīng)于一個(gè)APIManager,然后這個(gè)APIManager只需要提供參數(shù)就能起飛,API名字、著陸方式都已經(jīng)集成入APIManager中。比如這樣:
離散型API調(diào)用方式:
@property (nonatomic, strong) ItemListAPIManager *itemListAPIManager;
// getter
- (ItemListAPIManager *)itemListAPIManager {
if (_itemListAPIManager == nil) {
_itemListAPIManager = [[ItemListAPIManager alloc] init]; _itemListAPIManager.delegate = self;
}
return _itemListAPIManager;
}
// 使用的時(shí)候就這么寫:
[self.itemListAPIManager loadDataWithParams:params];
集約型API調(diào)用和離散型API調(diào)用這兩者實(shí)現(xiàn)方案不是互斥的,單看下層,大家都是集約型。因?yàn)榘l(fā)起一個(gè)API請(qǐng)求之后,除去業(yè)務(wù)相關(guān)的部分(比如參數(shù)和API名字等),剩下的都是要統(tǒng)一處理的:加密,URL拼接,API請(qǐng)求的起飛和著陸,這些處理如果不用集約化的方式來實(shí)現(xiàn),作者非癲即癡。
然而對(duì)于整個(gè)網(wǎng)絡(luò)層來說,尤其是業(yè)務(wù)方使用的那部分,我傾向于提供離散型的API調(diào)用方式,并不建議在業(yè)務(wù)層的代碼直接使用集約型的API調(diào)用方式。原因如下:
- 原因1:當(dāng)前請(qǐng)求正在外面飛著的時(shí)候,根據(jù)不同的業(yè)務(wù)需求存在兩種不同的請(qǐng)求起飛策略:一個(gè)是取消新發(fā)起的請(qǐng)求,等待外面飛著的請(qǐng)求著陸。另一個(gè)是取消外面飛著的請(qǐng)求,讓新發(fā)起的請(qǐng)求起飛。集約化的API調(diào)用方式如果要滿足這樣的需求,那么每次要調(diào)用的時(shí)候都要多寫一部分判斷和取消的代碼,手段就做不到很干凈。
前者的業(yè)務(wù)場(chǎng)景舉個(gè)例子就是刷新頁面的請(qǐng)求,刷新詳情,刷新列表等。后者的業(yè)務(wù)場(chǎng)景舉個(gè)例子是列表多維度篩選,比如你先篩選了商品類型,然后篩選了價(jià)格區(qū)間。當(dāng)然,后者的情況不一定每次篩選都要調(diào)用API,我們先假設(shè)這種篩選每次都必須要通過調(diào)用API才能獲得數(shù)據(jù)。
如果是離散型的API調(diào)用,在編寫不同的APIManager時(shí)候就可以針對(duì)不同的API設(shè)置不同的起飛策略,在實(shí)際使用的時(shí)候,就可以不必關(guān)心起飛策略了,因?yàn)锳PIMananger里面已經(jīng)寫好了。
- 原因2:便于針對(duì)某個(gè)API請(qǐng)求來進(jìn)行AOP。在集約型的API調(diào)用方式下,如果要針對(duì)某個(gè)API請(qǐng)求的起飛和著陸過程進(jìn)行AOP,這代碼得寫成什么樣。。。噢,尼瑪這畫面太美別說看了,我都不敢想。
對(duì)于原因2,不理解
- 原因3:當(dāng)API請(qǐng)求的著陸點(diǎn)消失時(shí),離散型的API調(diào)用方式能夠更加透明地處理這種情況。
當(dāng)一個(gè)頁面的請(qǐng)求正在天上飛的時(shí)候,用戶等了好久不耐煩了,小手點(diǎn)了個(gè)back,然后ViewController被pop被回收。此時(shí)請(qǐng)求的著陸點(diǎn)就沒了。這是很危險(xiǎn)的情況,著陸點(diǎn)要是沒了,就很容易crash的。一般來說處理這個(gè)情況都是在dealloc的時(shí)候取消當(dāng)前頁面所有的請(qǐng)求。如果是集約型的API調(diào)用,這個(gè)代碼就要寫到ViewController的dealloc里面,但如果是離散型的API調(diào)用,這個(gè)代碼寫到APIManager里面就可以了,然后隨著ViewController的回收進(jìn)程,APIManager也會(huì)被跟著回收,這部分代碼就得到了調(diào)用的機(jī)會(huì)。這樣業(yè)務(wù)方在使用的時(shí)候就可以不必關(guān)心著陸點(diǎn)消失的情況了,從而更加關(guān)注業(yè)務(wù)。
- 原因4:離散型的API調(diào)用方式能夠最大程度地給業(yè)務(wù)方提供靈活性,比如reformer機(jī)制就是基于離散型的API調(diào)用方式的。另外,如果是針對(duì)提供翻頁機(jī)制的API,APIManager就能簡(jiǎn)單地提供loadNextPage方法去加載下一頁,頁碼的管理就不用業(yè)務(wù)方去管理了。還有就是,如果要針對(duì)業(yè)務(wù)請(qǐng)求參數(shù)進(jìn)行驗(yàn)證,比如用戶填寫注冊(cè)信息,在離散型的APIManager里面實(shí)現(xiàn)就會(huì)非常輕松。
綜上,關(guān)于集約型的API調(diào)用和離散型的API調(diào)用,我傾向于這樣:對(duì)外提供一個(gè)BaseAPIManager來給業(yè)務(wù)方做派生,在BaseManager里面采用集約化的手段組裝請(qǐng)求,放飛請(qǐng)求,然而業(yè)務(wù)方調(diào)用API的時(shí)候,則是以離散的API調(diào)用方式來調(diào)用。如果你的App只提供了集約化的方式,而沒有離散方式的通道,那么我建議你再封裝一層,便于業(yè)務(wù)方使用離散的API調(diào)用方式來放飛請(qǐng)求。
摘自:
http://casatwy.com/iosying-yong-jia-gou-tan-wang-luo-ceng-she-ji-fang-an.html
感覺寫的真好,之前看過YTKNetwork也有類似的設(shè)計(jì),一個(gè)api對(duì)應(yīng)一個(gè)request。