资讯专栏INFORMATION COLUMN

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

kelvinlee / 2395人阅读

摘要:我们的客户端网络框架至少要解决三个问题实现通信协议账户系统简化服务端接口调用。账户系统简而言之就是实现注册登陆注销等功能,并维护登陆状态等。这样客户代码就可以通过这一单一接口使用网络框架了。

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

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

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

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

@protocol SFBackendInterfaces

-(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password completionHandler:(SFNetworkingTaskCompletionHandler);

-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler;

@end

@interface SFClient:NSObject

@interface SFBackendInterfacesImpl:NSObject

这样做的目的是什么呢,就是让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];

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/11728.html

相关文章

发表评论

0条评论

最新活动
阅读需要支付1元查看
<