iOS 关于MVC和MVVM设计模式的那些事
iOS 关于MVC和MVVM设计模式的那些事
一、概述在 iOS 开发中,MVC(Model View Controller)是构建iOS App的标准模式,是苹果推荐的一个用来组织代码的权威范式。Apple甚至是这么说的。在MVC下,所有的对象被归类为一个Model,一个View,和一个Controller。Model持有数据,View显示与用户交互的界面,而ViewController调解Model和View之间的交互。现在,MVC 依然是目前主流客户端编程框架,但同时它也被调侃成Massive View Controller(重量级视图控制器),想必开发者在开发中无可避免被下面几个问题所困扰:
厚重的ViewController遗失的网络逻辑(无立足之地)较差的可测试性为了避免和解决上述问题的产生,从MVC引申出来一种维护性较强、耦合性低的新的架构MVVM(Model View View-Mode),MVVM正式规范了视图和控制器紧耦合的性质,并引入新的组件。MVVM主要目的是为了分离视图(View)和模型(Model)。
本文只是分享一下笔者对MVC和MVVM的一些见解,在此抛砖引玉,希望能为存在对MVC和MVVM迷茫的广大开发者提供一点思路,少走一些弯路,填补一些细坑。文章仅供大家参考,若有不妥之处,还望不吝赐教,欢迎批评指正。
二、MVC(Model View Controller)MVC之间的关系任何一个正经开发过软件的人都熟悉
MVC
,它意思是Model View Controller, 是一个在复杂应用设计中组织代码的公认模式,它们之间的结构关系如下:MVC示意图.png我们看到的只是一个苹果 **典型的MVC ** 设置。
view
将用户交互通知给
controller
。
view controller
通过更新
model
来反应状态的改变。
model
(通常使用
Key-Value-Observation
)通知
controller
来更新他们负责的
view
。大多数iOS应用程序的代码使用这种方式来组织。然而,
典型的MVC架构
不适用于当下的iOS开发。尽管从技术上看
View
和
Controller
是相互独立的,但事实上它们几乎总是结对出现,一个
View
只能与一个
Controller
进行匹配,反之亦然。既然如此,那我们为何不正规化它们的连接:
MVC示意图2.png因此,M-VC 可能是对 iOS 开发中的
MVC
模式更为准确的解读,同时更也准确地描述了我们日常开发可能已经编写的
MVC
代码,但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。(PS:躺枪了没...)
举例说明:
M-VC_Example.png若假设笔者利用
MVC
的设计模式来开发此界面,那想必是这样的。
M:SUGoods(商品模型Model)
V:SUGoodsCell(展示商品数据的View,自定义的 UITableViewCell)
C:SUHomeViewController (首页控制器Controller)
控制器(
SUHomeViewController
)代码实现
- (void) requestRemoteData{ // 1.发起网络请求,获取到服务器的数据,并将其转化成模型数据(`SUGoods`) // 2.添加到数据源(`dataSource`) // 3.最后刷新表格`[self.tableView reloadData]`,配置`SUGoodsCel`l即可}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ SUGoodsCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Goods"]; cell.goods = self.dataSource[indexPath.row]; return cell;}
View(SUGoodsCell)
代码实现
SUGoodsCell.h@class SUGoods;@interface SUGoodsCell : UITableViewCell@property (nonatomic, strong) SUGoods *goods;@endSUGoodsCell.m@implementation SUGoodsCell- (void)setGoods:(SUGoods *)goods{ _goods = goods /// config data ....}@end
都写到这份上了,大家用脚趾头想想,这个
SUGoodsCell
,正是由
View
直接来调用
Model
,所以事实上
典型的MVC
的原则已经违背了,但是这种情况是一直发生的甚至于人们不觉得这里有哪些不对。如果严格遵守
MVC
的话,你会把对
cell
的设置放在
Controller
中,不向
View
传递一个
Model
对象,这样就会大大增加
Controller的体积
。所以说,这哪里是
典型的MVC
设计模式,这分明是
M-VC
设计模式呀。简而言之,在理想的世界里,
MVC
也许工作的很好。然而,我们生活在真实的世界,谢谢(PS:让梦想实现的最好的方式,就是醒来!!!)。
MVC的弊端
MVC
的利弊大家想必是有目共睹的,
Massive View Controller
的说法也并非空穴来风的。让我们一起探讨
MVC
的弊端,剖析问题产生原因,打造一个轻量级的
ViewController
,明确
MVC
设计模式中各个角色的职责。厚重的View ControllerM:模型model的对象通常非常的简单。根据Apple的文档,
model
应包括
数据
和
操作数据的业务逻辑
。而在实践中,
model
层往往非常薄,不管怎样,
model
层的业务逻辑不应被拖入到
controller
。V:视图
view
通常是
UIKit
控件(
component
,这里根据习惯译为控件)或者编码定义的
UIKit
控件的集合。
View
的如何构建(PS:
IB或者手写界面
)何必让
Controller
知晓,同时
View
不应该直接引用
model
(PS:现实中,你懂的!),并且仅仅通过
IBAction事件
引用
controller
。业务逻辑很明显不归入
view
,视图本身没有任何业务。C:控制器
controller
。
Controller
是app的“胶水代码”:协调模型和视图之间的所有交互。控制器负责管理他们所拥有的视图的视图层次结构,还要响应视图的
loading
、
appearing
、
disappearing
等等,同时往往也会充满我们
不愿暴露的model的模型逻辑
以及
不愿暴露给视图的业务逻辑
。网络数据的请求及后续处理,本地数据库操作,以及一些带有工具性质辅助方法都加大了
Massive View Controller
的产生。
遗失(无处安放)的网络逻辑苹果使用的
MVC
的定义是这么说的:所有的对象都可以被归类为一个
model
,一个
view
,或是一个
controller
。你可能试着把它放在
Model
对象里,但是也会很棘手,因为网络调用应该使用异步,这样如果一个网络请求比持有它的
model
生命周期更长,事情将变的复杂。显然
View
里面做网络请求那就更格格不入了,因此只剩下
Controller
了。若这样,这又加剧了
Massive View Controller
的问题。若不这样,何处才是
网络逻辑
的家呢?
较差的可测试性由于
View Controller
混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。若一个
Massive View Controller
有上万行代码,要你编写个单元测试,我敢保证,你不是想写,你是想死,分分钟填表走人。
三、MVVM(Model View View-Mode)一种可以很好地解决
Massive View Controller
问题的办法就是将
Controller
中的展示逻辑抽取出来,放置到一个专门的地方,而这个地方就是
viewModel
。
MVVM
衍生于
MVC
,是对
MVC
的一种演进,它促进了 UI 代码与业务逻辑的分离。它正式规范了视图和控制器紧耦合的性质,并引入新的组件。他们之间的结构关系如下:
MVVM示意图.pngMVVM 的基本概念
在
MVVM
中,
view
和
view controller
正式联系在一起,我们把它们视为一个组件
view
和
view controller
都不能直接引用
model
,而是引用视图模型(
viewModel
)
viewModel
是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他代码的地方使用
MVVM
会轻微的增加代码量,但总体上减少了代码的复杂性MVVM 的注意事项
view
引用
viewModel
,但反过来不行(即不要在
viewModel
中引入
#import UIKit.h
,任何视图本身的引用都不应该放在
viewModel
中)(PS:基本要求,必须满足)
viewModel
引用
model
,但反过来不行MVVM 的使用建议
MVVM
可以兼容你当下使用的
MVC
架构。
MVVM
增加你的应用的可测试性。
MVVM
配合一个绑定机制效果最好(PS:ReactiveCocoa你值得拥有)。
viewController
尽量不涉及业务逻辑,让
viewModel
去做这些事情。
viewController
只是一个中间人,接收
view
的事件、调用
viewModel
的方法、响应
viewModel
的变化。
viewModel
绝对不能包含视图
view(UIKit.h)
,不然就跟
view
产生了耦合,不方便复用和测试。
viewModel
之间可以有依赖。
viewModel
避免过于臃肿,否则重蹈
Controller
的覆辙,变得难以维护。MVVM 的优势
低耦合:
View
可以独立于
Model
变化和修改,一个
viewModel
可以绑定到不同的
View
上可重用性:可以把一些视图逻辑放在一个
viewModel
里面,让很多
view
重用这段视图逻辑独立开发:开发人员可以专注于业务逻辑和数据的开发
viewModel
,设计人员可以专注于页面设计可测试:通常界面是比较难于测试的,而
MVVM
模式可以针对
viewModel
来进行测试MVVM 的弊端
数据绑定使得
Bug
很难被调试。你看到界面异常了,有可能是你
View
的代码有
Bug
,也可能是
Model
的代码有问题。数据绑定使得一个位置的
Bug
被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。对于过大的项目,数据绑定和数据转化需要花费更多的内存(成本)。主要成本在于:数组内容的转化成本较高:数组里面每项都要转化成
Item
对象,如果Item对象中还有类似数组,就很头疼。转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。只有在API返回的数据高度标准化时,这些对象原型(
Item
)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。调试时通过对象原型查看数据内容不如直接通过
NSDictionary/NSArray
直观。同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。四、总结
MVC
的设计模式也并非是病入膏肓,无药可救的架构,最起码目前
MVC
设计模式仍旧是iOS开发的主流框架,存在即合理。针对文章所述的弊端,我们依旧有许多可行的方法去避免和解决,从而打造一个轻量级的
ViewController
。
MVVM
是
MVC
的升级版,完全兼容当前的
MVC
架构,
MVVM
虽然促进了UI 代码与业务逻辑的分离,一定程度上减轻了
ViewController
的臃肿度,但是
View
和
ViewModel
之间的数据绑定使得
MVVM
变得复杂和难用了,如果我们不能更好的驾驭两者之间的数据绑定,同样会造成
Controller
代码过于复杂,代码逻辑不易维护的问题。一个轻量级的
ViewController
是基于
MVC
和
MVVM
模式进行代码职责的分离而打造的。
MVC
和
MVVM
有优点也有缺点,但缺点在他们所带来的好处面前时不值一提的。他们的低耦合性,封装性,可测试性,可维护性和多人协作便利大大提高了开法效率。同时,我们需要保持的是一个拥抱变化的心,以及理性分析的态度。在新技术的面前,不盲从,也不守旧,一切的决策都应该建立在认真分析的基础上,这样才能应对技术的变化。五、期待文章若对您有点帮助,请给个喜欢??,毕竟码字不易;若对您没啥帮助,请给点建议
发表评论