轉(zhuǎn)載請(qǐng)注明出處:http://www.olinone.com/
接口是一系列可調(diào)用方法的集合。何為接口編程?接口編程是指當(dāng)寫(xiě)一個(gè)函數(shù)或一個(gè)方法時(shí),我們應(yīng)該更加關(guān)注具體的接口,而不是實(shí)現(xiàn)類。具體理解可以參考這篇文章
在OC中,接口又可以理解為Protocol,面向接口編程又可以理解為面向Protocol編程,或者面向協(xié)議編程。在Swift中,蘋(píng)果大幅強(qiáng)化了 Protocol 在這門(mén)語(yǔ)言中的地位,整個(gè) Swift 標(biāo)準(zhǔn)庫(kù)也是基于 Protocol 來(lái)設(shè)計(jì)的,有興趣的童鞋可以看看這篇文章。面向接口編程正逐步成為程序開(kāi)發(fā)的主流思想
在實(shí)際開(kāi)發(fā)中,大多數(shù)朋友都比較熟悉對(duì)象編程,比如,使用ASIHttpRequest執(zhí)行網(wǎng)絡(luò)請(qǐng)求
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDidFinishSelector:@selector(requestDone:)];
[request setDidFailSelector:@selector(requestWrong:)];
[request startAsynchronous];
request是請(qǐng)求對(duì)象,當(dāng)發(fā)起請(qǐng)求時(shí),調(diào)用者需要知道給對(duì)象賦哪些屬性或者調(diào)用對(duì)象哪些方法。然而,使用AFNetworking請(qǐng)求方式卻不盡相同
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
}];
同是請(qǐng)求對(duì)象,使用AFNetworking發(fā)起請(qǐng)求時(shí),調(diào)用者可以不需要關(guān)心它有哪些屬性,只有接口無(wú)法滿足需求時(shí)才需要了解相關(guān)屬性的定義。兩種設(shè)計(jì)思路完全不同,當(dāng)然,此處并不是想表明孰優(yōu)孰劣,只是想通過(guò)兩種截然不同的請(qǐng)求方式引出本文的思想——面向接口編程(或者面向協(xié)議編程)
接口比屬性直觀
在對(duì)象編程中,定義一個(gè)對(duì)象時(shí),往往需要為其定義各種屬性。比如,ReactiveCocoa中RACSubscriber對(duì)象定義如下
@interface RACSubscriber ()
@property (nonatomic, copy) void (^next)(id value);
@property (nonatomic, copy) void (^error)(NSError *error);
@property (nonatomic, copy) void (^completed)(void);
@end
參考AFNetworking的思想,以接口的形式提供訪問(wèn)入口
@interface RACSubscriber
+ (instancetype)subscriberWithNext:(void (^)(id x))next
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?error:(void (^)(NSError *error))error?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?completed:(void (^)(void))completed;
@end
通過(guò)接口的定義,調(diào)用者可以忽略對(duì)象的屬性,聚焦于其提供的接口和功能上。程序猿在首次接觸陌生的某個(gè)對(duì)象時(shí),接口往往比屬性更加直觀明了,抽象接口往往比定義屬性更能描述想做的事情
接口依賴
設(shè)計(jì)一個(gè)APIService對(duì)象
@interface ApiService : NSObject
@property (nonatomic, strong) NSURL????????*url;
@property (nonatomic, strong) NSDictionary *param;
- (void)execNetRequest;
@end
正常發(fā)起Service請(qǐng)求時(shí),調(diào)用者需要直接依賴該對(duì)象,起不到解耦的目的。當(dāng)業(yè)務(wù)變動(dòng)需要重構(gòu)該對(duì)象時(shí),所有引用該對(duì)象的地方都需要改動(dòng)。如何做到既能滿足業(yè)務(wù)又能兼容變化?抽象接口也許是一個(gè)不錯(cuò)的選擇,以接口依賴的方式取代對(duì)象依賴,改造代碼如下
@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
通過(guò)接口的定義,調(diào)用者可以不再關(guān)心ApiService對(duì)象,也無(wú)需了解其有哪些屬性。即使需要重構(gòu)替換新的對(duì)象,調(diào)用邏輯也不受任何影響。調(diào)用接口往往比訪問(wèn)對(duì)象屬性更加穩(wěn)定可靠
抽象對(duì)象
定義ApiServiceProtocol可以隱藏ApiService對(duì)象,但是受限于ApiService對(duì)象的存在,業(yè)務(wù)需求發(fā)生變化時(shí),仍然需要修改ApiService邏輯代碼。如何實(shí)現(xiàn)在不修改已有ApiService業(yè)務(wù)代碼的條件下滿足新的業(yè)務(wù)需求?
參考Swift抽象協(xié)議的設(shè)計(jì)理念,可以使用Protocol抽象對(duì)象,畢竟調(diào)用者也不關(guān)心具體實(shí)現(xiàn)類。Protocol可以定義方法,可是屬性的問(wèn)題怎么解決?此時(shí),裝飾器模式也許正好可以解決該問(wèn)題,讓我們?cè)囍^續(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)過(guò)Protocol的改造,ApiService對(duì)象化身為ApiService接口,其不再依賴于任何對(duì)象,做到了真正的接口依賴取代對(duì)象依賴,具有更強(qiáng)的業(yè)務(wù)兼容性
定義一個(gè)Get請(qǐng)求對(duì)象
@interface GetApiService : NSObject ?
@end
@implementation GetApiService
- (void)requestNetWithUrl:(NSURL *)url Param:(NSDictionary *)param {
? ? // to do
}
@end
請(qǐng)求代碼也隨之改變
@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
對(duì)象可以繼承對(duì)象,Protocol也可以繼承Protocol,并且可以繼承多個(gè)Protocol,Protocol具有更強(qiáng)的靈活性。某一天,業(yè)務(wù)需求變更需要用到新的Post請(qǐng)求時(shí),可以不用修改 GetApiService一行代碼,定義一個(gè)新的 PostApiService實(shí)現(xiàn)Post請(qǐng)求即可,避免了對(duì)象里面出現(xiàn)過(guò)多的if-else代碼,也保證了代碼的整潔性
依賴注入
文章寫(xiě)到這里,細(xì)心的童鞋可能已經(jīng)發(fā)現(xiàn)問(wèn)題——GetApiService依然是以對(duì)象依賴的形式存在。如何解決這個(gè)問(wèn)題?沒(méi)錯(cuò),那就是依賴注入!
依賴注入是什么?借用博客里面的一句話,其最大的特點(diǎn)就是:幫助我們開(kāi)發(fā)出松散耦合、可維護(hù)、可測(cè)試的代碼和程序。這條原則的做法是大家熟知的面向接口,或者說(shuō)是面向抽象編程。objc上這篇文章介紹的不錯(cuò), 有興趣的童鞋也可以看看,在此就不再累述
基于依賴注入objection開(kāi)源庫(kù)的基礎(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)心請(qǐng)求接口,實(shí)現(xiàn)者關(guān)心需要實(shí)現(xiàn)的接口,各司其職,互不干涉
接口和實(shí)現(xiàn)分離的設(shè)計(jì)適用于團(tuán)隊(duì)協(xié)作開(kāi)發(fā),實(shí)現(xiàn)了系統(tǒng)的松散耦合,便于以后升級(jí)擴(kuò)展。當(dāng)然,接口編程對(duì)開(kāi)發(fā)人員的要求也比較高,需要提前定義好接口,接口一變,全部亂套,這就是所謂的設(shè)計(jì)比實(shí)現(xiàn)難,但是設(shè)計(jì)接口的人工資都高啊?。。?/p>
后記:你的支持是我前進(jìn)的最大動(dòng)力,你可以在github找到我,也可以通過(guò)微博聯(lián)系我,感謝你的來(lái)訪!