咱们的客户端网络框架至少要解决三个问题:实现通讯协议、帐户系统、简化服务端接口调用。后端
实现通讯协议 根据与服务端制定的通讯协议,实现请求的组装,序列化,发送,以及响应的接收和解析等。
帐户系统 简而言之就是实现注册、登录、注销等功能,并维护登录状态等。
简化服务端接口调用 客户代码只须要提供业务参数和回调函数就能够实现与服务器通讯,网络框架负责封装掉其他全部细节。设计模式
我想对架构比较敏感的读者会马上有这样的共鸣,首先上述的帐户系统显然是一个独立的模块,最好单独设计实现。另外一方面,帐户系统的功能又是以服务端接口调用为基础的,在形式上登录操做也是调用服务端接口,那么把登录相关操做与其余服务端接口调用实现于一处就是天然的。若是再做一些考虑,咱们还会想到的一个问题是,网络框架暴露给客户代码的接口应当尽量单一,若是咱们用一个类维护帐户系统,用另外一个类作服务端业务接口调用,会嫌不够简洁。达成这几点共识以后,咱们就能够继续探讨一些设计细节了。先看下面的代码。服务器
//SFClient.h @interface SFClient @property (nonatomic,readonly) NSString* account; @property (nonatomic,readonly) NSString* password; @property (nonatomic,readonly) BOOL isLoggedIn; @property (nonatomic,readonly) BOOL pendingLogin; @property (nonatomic,readonly) NSString* sessionId; -(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password; -(NSURLSessionTask*)logout; -(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler; //... @end
有的同行习惯于为每个后端接口单独开一个类,这固然也不失为一种设计风格,笔者也曾尝试过,我的感受嫌繁。
这里的SFClient类做为帐户系统,又兼具服务端业务接口调用功能,实现了使接口尽量简洁的设计目标,却违背了帐户系统应当单独实现的架构设计直觉。
如何解决这一矛盾呢?能够采用dynamic proxy设计模式。网络
定义一个protocol
假设叫SFBackendInterfaces
,和一个实现类假设叫SFBackendInterfacesImpl
。让SFClient
和SFBackendInterfacesImpl
都实现这个协议。session
@protocol SFBackendInterfaces<NSObject> -(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password completionHandler:(SFNetworkingTaskCompletionHandler); -(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler; @end @interface SFClient:NSObject<SFBackendInterfaces> @interface SFBackendInterfacesImpl:NSObject<SFBackendInterfaces>
这样作的目的是什么呢,就是让SFClient类继续提供服务端接口调用功能,同时把这些接口调用的实现代码交给SFBackendInterfacesImpl。这样就既知足网络框架接口简洁的需求,又保持了SFClient类做为帐户系统的纯净,-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler;
这行代码能够从SFClient的interface中拿掉了,而且相关代码也不须要出如今它的implementation文件里了。咱们来看implementation。架构
@implementation SFClient -(void)forwardInvocation:(NSInvocation *)anInvocation { if([self.backendInterfacesImpl respondsToSelector:anInvocation.selector]){ [anInvocation invokeWithTarget:self.backendInterfacesImpl]; }else{ [super forwardInvocation:anInvocation]; } } -(void)loginWithAccount:(NSString*)account password:(NSString*)password { NSURLSessionTask* task=[self loginWithAccount:account password:(NSString*)password completionHandler:^(SFResponse* response){ [[NSNotificationCenter defaultCenter] postNotificationNamed:SFLoginCompletionNotification object:response]; _pendingLogin=NO; if(response.status==SFResponseStatusSuccess){ _loggedIn=YES; } }]; [task resume]; _pendingLogin=YES; } @end @implementation SFBackendInterfacesImpl -(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password completionHandler:(SFNetworkingTaskCompletionHandler) { //... } -(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler { //... } @end
这样客户代码就能够经过SFClient这一单一接口使用网络框架了。框架
[[SFClient sharedClient] loginWithAccount:xxxx password:xxxx]; //... NSURLSessionTask* task=[[SFClient sharedClient] someNetworkingTaskWithPara::param completionHandler:^(SFResponse* response){ //... }]; [task resume];
而在框架内部实现上,帐户系统和业务接口调用的实现仍然是分离的。函数