iOS客户端网络框架设计(一)

咱们的客户端网络框架至少要解决三个问题:实现通讯协议、帐户系统、简化服务端接口调用。后端

实现通讯协议 根据与服务端制定的通讯协议,实现请求的组装,序列化,发送,以及响应的接收和解析等。
帐户系统 简而言之就是实现注册、登录、注销等功能,并维护登录状态等。
简化服务端接口调用 客户代码只须要提供业务参数和回调函数就能够实现与服务器通讯,网络框架负责封装掉其他全部细节。设计模式

我想对架构比较敏感的读者会马上有这样的共鸣,首先上述的帐户系统显然是一个独立的模块,最好单独设计实现。另外一方面,帐户系统的功能又是以服务端接口调用为基础的,在形式上登录操做也是调用服务端接口,那么把登录相关操做与其余服务端接口调用实现于一处就是天然的。若是再做一些考虑,咱们还会想到的一个问题是,网络框架暴露给客户代码的接口应当尽量单一,若是咱们用一个类维护帐户系统,用另外一个类作服务端业务接口调用,会嫌不够简洁。达成这几点共识以后,咱们就能够继续探讨一些设计细节了。先看下面的代码。服务器

//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。让SFClientSFBackendInterfacesImpl都实现这个协议。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];

而在框架内部实现上,帐户系统和业务接口调用的实现仍然是分离的。函数

相关文章
相关标签/搜索