轉載請注明出處: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ā)人員的要求也比較高,需要提前定義好接口糜芳,接口一變飒货,全部亂套魄衅,這就是所謂的設計比實現難,但是設計接口的人工資都高疤粮ā;纬妗!扣墩!