谈谈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 ...
随机推荐
- 如何使用UDP进行跨网段广播
广播域首先我们来了解一下广播域的概念.广播域是网络中能接收任一台主机发出的广播帧的所有主机集合.也就是说,如果广播域内的其中一台主机发出一个广播帧,同一广播域内所有的其它主机都可以收到该广播帧.广播域 ...
- java中接口与多重继承的关系
在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制.正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力.abstract class和int ...
- POJ 1947-Rebuilding Roads(树形背包)
题意: 一个树求得到一个节点数为p的子树,最小需要删除的边数. 分析:父节点到儿子这条边,删或不删,背包问题. #include <map> #include <set> #i ...
- Jquery+asp.net后台数据传到前台js进行解析的方法
所以在解析后台数据的时候,我们需要根据后台的数据情况,特殊处理和对待. 我这里后台用的是asp.net提供的wcf服务,也有ashx一般处理程序.大致原理差不多. C#中我们经常用的对象,有实体对象比 ...
- PHP 进行蜘蛛访问日志统计
$useragent = addslashes(strtolower($_SERVER['HTTP_USER_AGENT'])); if (strpos($useragent, 'googlebot' ...
- O(1)时间删除链表的已知结点
这题并不需要从头结点遍历到已知结点,只需要知道已知结点,将改结点下一个结点赋值给它,再删除这个下一个结点就行,其中还需要考虑各种情况. 1)链表为空或者已知结点为空 2)链表只有一个结点,这个结点就是 ...
- Objective-C之消息机制
话说2014年4月编程语言排行榜中Objective-C的使用比又增加了,看来IOS和MAX OS的开发者是真给力呀. 不过个人感觉用不了多久,IOS和Android的开发者收入就不会有那么大的差异了 ...
- Mac java环境配置
进入命令行 cd ~ touch .bash_profile vi .bash_profile 输入内容jdk变量配置内容: export JAVA_HOME=/Library/Java/JavaVi ...
- HW7.8
import java.util.ArrayList; import java.util.Scanner; public class Solution { public static void mai ...
- Android实例-操作sqlite数据之自建导航(XE8+小米2)
相关资料: 源文:http://blog.sina.com.cn/s/blog_77691fb90101g9hh.html help://embarcadero.rs_xe5/rad/Mobile_T ...