谈谈MVVM和链式网络请求架构
前言
前一段时间一直在学习iOS的架构。为什么呢?
公司的架构一直是MVC,当我们正式上线的时候,项目已经有了超十万行代码。主要的VC一般都有2000行代码以上。
关键是,目前版本我们只做了三分之一的业务逻辑…
所以,架构重构吧。
正文
MVVM
MVVM: Model-View-ViewModel
MVVM其实是MVC的进化版,它将业务逻辑从VC中解耦到ViewModel,来实现VC大’瘦身’。
用代码解释吧!
做一个简单的登录判断:
创建LoginViewModel(逻辑处理),LoginModel(只放数据),LoginViewController。
这里不用LoginView是为了让初学者能更好的把精力集中在用ViewModel解耦上。
当然要是你这些都明白,你可以直接看Wzxhaha/RandomerFramework,这是我在做的独立项目Randomer的基本架构(SubClasses+Protocol+MVVM+RAC)以及它的登录注册模块。另外,感谢王隆帅的这篇文章为我打开了新世界的大门。
在LoginModel中加入方法
//.h
- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password;
@property (nonatomic,copy,readonly)NSString * username;
@property (nonatomic,copy,readonly)NSString * password;
//.m
- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password {
if (self = [super init]) {
_username = username;
_password = password;
}
return self;
}
这个没什么好讲的,就是给Model加一个初始化方法。
在LoginViewModel中加入方法
#import "PersonModel.h"
- (instancetype)initWithPerson:(PersonModel *)person;
@property (nonatomic,assign,readonly)BOOL canLogin;
- (instancetype)initWithPerson:(PersonModel *)person {
if (self = [super init]) {
//在这做你绑定model后的处理
_canLogin = [self valiCanLoginWithUserName:person.username password:person.password];
}
return self;
}
- (BOOL)valiCanLoginWithUserName:(NSString *)username password:(NSString *)password {
if (username.length & password.length) {
return YES;
} else {
return NO;
}
}
给ViewModel添加个绑定Model的初始化方法,以及判断帐号密码是否有效的方法。
然后VC(或者View)就可以直接这样获得判断后的结果
PersonModel * person = [[PersonModel alloc]initWithUserName:@"10" password:@"10"];
PersonViewModel * viewModel = [[PersonViewModel alloc]initWithPerson:person];
NSLog(@"%d",viewModel.canLogin);
简单的功能的时候没什么,当你处理复杂的逻辑判断的时候,MVVM会有巨大优势。
顺便讲一下ReactiveCocoa,我之所以这么推崇MVVM,主要就是因为RAC和MVVM简直太配了!
ReactiveCocoa
RAC具有函数式编程和响应式编程的特性,要是对编程思想不熟的可以看我的WZXProgrammingIdeas
RAC最大的用处就是能监听到各个事件,RAC把这个叫做信号流,然后接受信号通过block回调,里面大量的使用了block,所以一定要用好@weakify(self)和@strongify(self)。
为什么说RAC和MVVM太配了?
MVVM是把方法解耦到ViewModel,但是还是要VC(V)调用的,那么判断什么时候调用的逻辑还是会复杂。
而RAC解决了这个问题,它负责监听事件,然后调用ViewModel来进行逻辑判断。
例如:
[[_registerBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
@strongify(self)
[self.viewModel toRegisterWithType:Register];
}];
[[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
@strongify(self)
[self.viewModel loginWithUserName:self.usernameTextField.text password:self.usernameTextField.text Success:^(id response) {
} failure:^{
SHOW_ERROR(@"错误", @"账号或密码错误")
} error:^(NSError *error) {
SHOW_ERROR(@"错误", @"网络连接失败")
}];
}];
RAC监听了登录和注册按钮,使得代码简洁,而且结构十分紧凑。
Demo的话还是看这个吧Wzxhaha/RandomerFramework
https://github.com/Wzxhaha/RandomerFramework
或者简单版的WZXRACDemo
https://github.com/Wzxhaha/WZXRACDemo
链式网络请求框架
为什么封装WZXNetworking
这是一个容错性非常吓人的框架。
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").RequestType(POST).HTTPHeader(nil).Parameters(nil).RequestSerialize(RequestSerializerHTTP).ResponseSerialize(ResponseSerializerJSON) startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
在这里除了.setRequest(url)和startRequestWithSuccess failure方法,其他都是非必要的。
你可以这样:
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001") startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
链式在参数和参数的选择很多的情况或者很有可能改动的情况下展现了惊人的优势。因为,它的改动十分方便,只不过添加或者修改一个方法。
打个比方:
换成集中式API封装应该是这样的:
- (void)GET:(NSString *)url
parameters:(id)Parameters
success:(SuccessBlock)success
failure:(FailureBlock)failure;
当你要添加一个Version属性做API版本判断的时候,你能怎么办?只能重写方法,在方法中加入一个Version参数,然后所有使用的网络请求都要改变方法。
换成分布式API封装我们则不考虑对比了..
GeneralAPI *apiGeGet = [[GeneralAPI alloc] initWithRequestMethod:@"get"];
apiGeGet.apiRequestMethodType = RequestMethodTypeGET;
apiGeGet.apiRequestSerializerType = RequestSerializerTypeHTTP;
apiGeGet.apiResponseSerializerType = ResponseSerializerTypeHTTP;
[apiGeGet setApiCompletionHandler:^(id responseObject, NSError * error) {
NSLog(@"responseObject is %@", responseObject);
if (error) {
NSLog(@"Error is %@", error.localizedDescription);
}
}];
[apiGeGet start];
这样的结构是否太松散?
再换成WZXNetworking
我们要做的只是再添加一个方法和一个成员变量,然后在原有方法后面加一个.method()
- (WZXNetworkManager * (^) (id some))method {
return ^WZXNetworkManager (id some) {
self.XXX = some
return self;
}
}
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").method(some) startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
代码放这:WZXNetworking
https://github.com/Wzxhaha/WZXNetworking
至于链式是怎么实现的,还是看那个WZXProgrammingIdeas
https://github.com/Wzxhaha/WZXProgrammingIdeas
谈谈MVVM和链式网络请求架构的更多相关文章
- Swift基础之使用Alamofire库进行网络请求和断点下载
好久没有写过Swift相关的文章博客了,这里我就展示一下关于使用Alamofire库的方法 1.什么是Alamofire (1)Alamofire 的前身是 AFNetworking.AFNetwor ...
- 简谈 JavaScript、Java 中链式方法调用大致实现原理
相信,在 JavaScript .C# 中都见过不少链式方法调用,那么,其中实现该类链式调用原理,大家有没有仔细思考过?其中 JavaScript 类库:jQuery 中就存在大量例子,而在 C# 中 ...
- 2020,最新APP重构:网络请求框架
在现在的app,网络请求是一个很重要的部分,app中很多部分都有或多或少的网络请求,所以在一个项目重构时,我会选择网络请求框架作为我重构的起点.在这篇文章中我所提出的架构,并不是所谓的 最好 的网络请 ...
- Rxjava2.0 链式请求异常处理
使用Rxjava2.0的过程中,难免会遇到链式请求,而链式请求一般都是第一个抛异常,那么后面的请求都是不会走的.现在来讨论一下链式请求的一种异常处理方法.例如: 一个登录-->通过登录返回的to ...
- 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别
一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...
- ios MVVM实践 刷新网络请求+tableView展示数据
[实现效果] [目录结构相关] 此示例展示用的是MVVM结构形式,表述如下 M:数据Model的存储,可以用来对属性进行处理.(即胖model概念,上图中xx万人订阅这个处理方法写在Model内) V ...
- Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析
Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...
- 谈谈 Objective-C 链式语法的实现
引言 对于 Objective-C 的语法,喜欢的人会觉得它是如此的优雅,代码可读性强,接近自然语言,开发者在调用大多数方法时不需要去查看注释或文档,通常只凭借方法名就可以大致知道这个方法的作用,可以 ...
- 使用Retrofit2+RxJava2+ProtoBuf实现网络请求
引言 Retrofit 是一个用于 Android 和 Java 平台的类型安全的,底层使用OkHttp实现网络请求框架.Retrofit 通过将 API 抽象成 Java 接口而让我们连接到 RES ...
随机推荐
- NopCommerce架构分析之四----插件机制
NopCommerce支持灵活的插件机制,所谓Web系统插件,其实也就是可以像原系统的一部分一样使用. Web系统的使用方式就是客户端发送一个请求,服务端进行解析.在asp.net MVC中对客户请求 ...
- puppet学习:类与类的依赖关系的问题
今天在部署Zabbix的Proxy时,在负责安装的Exec中去掉了一些无关的Package的依赖,结果,就出现了依赖关系的问题. 在zabbix::install中,我写的是require mysql ...
- 设计模式_Mediator_调停者模式
形象例子: 四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话.调停者模式: 调停者 ...
- Leetcode OJ : Triangle 动态规划 python solution
Total Accepted: 31557 Total Submissions: 116793 Given a triangle, find the minimum path sum from ...
- 50道经典的JAVA编程题 (11-15)
50道经典的JAVA编程题 (11-15),新年的第一天,继续啦...\(^o^)/~,这50道题都跨年了啊...哈哈 [程序11] TestTN.java 题目:有1.2.3.4个数字,能组成多少个 ...
- Ext.useShims=true
Extjs的panel中嵌套ActiveX的插件,如PDF,但是Ext控件被遮罩 eg.在panel的tbar中加入下拉框,结果其下拉值看不到,原因就是被PDF给遮住了, 此时只需设置Ext.useS ...
- Sort--快速排序
快速排序 1 public class QuickSort{ 2 3 public static int Partition(int[] a,int low,int high){ 4 int pivo ...
- CentOS 5.6 安装Oracle Java 和 Eclipse
1.卸载原有OpenJDK 1. 使用java -version查看当前Java版本信息 2. 使用rpm -qa | grep java 列出所有被安装的java rpm package ...
- P、NP及NPC问题
关于计算理论的一些概念 —判定问题和最优化问题 —归约 —多项式时间 —抽象问题 —形式语言体系 NPC证明 —一个问题转换为判定问题 —说明问题是NP —一个NPC问题规约到这个问题 —只需要规约到 ...
- Android 开发Project中各个目录和文件的介绍
如上图标号: 存放java文件的文件夹“src”: 由aapt工具根据应用中的资源文件自动生成的R.java文件,以及buildConfiger.java文件,这两个文件最好不要去修改: 存放各种资源 ...