在iOS开发中,我们接触比较多的是MVC架构,下面我们先来分析一下MVC架构。
1.MVC
MVC是一种软件架构模式,在1978年由Trygve Reenskaug提出,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller):
- 视图(View):用户界面。
- 控制器(Controller):业务逻辑处理,协调所有的工作。它使数据从模型传出来然后显示在视图上,监听事件,在必要的时候操作数据。
- 模型(Model):用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。
1.1iOS中的MVC
MVC是我们在客户端开发中最常见的模式,在iOS中,模型、视图、控制器之间的相互调用有一些规范和约定,借用斯坦福大学公开课第一课的一张截图,如下所示:
对于上图的理解,有如下几点:
- 先说明一下上图中的三种线:黄色实线、白色实线、白色虚线。可以看到,Model和View之间是两条黄色实线,这表示不能穿越这黄线,任何一个方向都不行,即Model和View完全分离。白色虚线表示可以自由的穿越它,只要是安全的。白色实线表示可以穿越,但需要交过路费。
- 绿色的箭头表示直接引用。简单的说,就是包含引用类的头文件和类的实例变量。上图中只有 Controller 中,有对 Model 和 View 的直接引用。
- View对Controller的通信有三种方式:Target-Action(如按钮的点击事件)、delegate(如表格的UITableViewDelegate)、data source(如表格的UITableViewDataSource)。通过这三种方式,View 达到了既能向 Controller 通信,又不需要知道具体的 Controller ,从而达到解耦的目的。
- Model 主要是通过 Notification 和 KVO 来和 Controller 通信的。
1.2MVC的优点
-
低耦合和高重用性。View和Model完全隔离,这样我们既可以很容易让同一套Model来适配不同的View(比如同时做iPad版和iPhone版),也可以很容易让多套Model在同一个View上展现(比如说根据数据源不同显示表格)。
-
可维护性。各层分离,使得应用更易于维护和修改。
1.3MVC的缺点
上图介绍的MVC是一种理想化的MVC,在我们实际项目中,MVC常常是这样的:
在iOS的项目开发中,View和Controller分的并不是特别清楚,导致写出臃肿的ViewController,最终让人感觉不是MVC,而是Model+ViewController。出现这种情况是为什么呢?这是因为UIViewController中自带了一个View,且控制了View的整个生命周期(viewDidLoad,viewWillAppear...)。自带的这个View,不光可以展示视图,更主要的任务是作为一个容器,来添加别的视图。
1.4怎样划分MVC的职责?
MVC是最基本的架构,在MVC的规范下,可以演化出其他的各种架构,如对胖Controller的拆分成MVCS,拆分胖Model成MVVM等等。不管怎么拆分,一定符合MVC规范。这里先继续介绍怎样划分MVC的职责,能比较好的规避臃肿ViewController的问题。
- Model的职责
- 给Controller提供数据;
- 给Controller存储数据;
- 提供业务基本组件(可以这样来理解基本组件:把Controller里面的私有方法单独封装出来),供Controller调用。
- View的职责
- 显示页面视图;
- 响应与业务无关的事件,比如说动画效果、视图更新等等。
- Controller的职责
- 管理视图容器的生命周期;
- 负责生成所有的View实例,并放入视图容器;
- 监听来自View与业务有关的事件,通过与Model的合作,来完成对应事件的响应。
1.5MVC小结
从上面的分析,我们可以看到:MVC是一种很优秀的架构,但它也存在一些问题,最大的问题是导致写出臃肿的ViewController,当前我们的做法是引入了一个DataHelper类来分解一部分VC里面的职能。有没有其他的方案呢?我们继续来分析。
2.MVVM
MVVM,Model-View-ViewModel,一个从 MVC 模式中进化而来的设计模式,最早于2005年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出。在 iOS 开发中实践 MVVM 的话,通常会把大量原来放在 ViewController 里的视图逻辑和数据逻辑移到 ViewModel 里,从而有效的减轻了 ViewController 的负担。另外通过分离出来的 ViewModel 获得了更好的测试性,我们可以针对 ViewModel 来测试,解决了界面元素难于测试的问题。MVVM 通常还会和一个强大的绑定机制一同工作,一旦 ViewModel 所对应的 Model 发生变化时,ViewModel 的属性也会发生变化,而相对应的 View 也随即产生变化。
2.1MVVM介绍
2.2MVVM的优点
-
减轻了VC的负担;
-
提高了可测试性;
-
强大的绑定机制。
2.3MVVM的缺点
- 学习成本和开发成本较高;
- 数据绑定使得BUG更难调试;
- VM的职责过重。
3.MVVMWithDC
3.1View和ViewController
- 每一个View都有一个与之对应的ViewModel,View的数据展示和样式都由它确定;
- 不引入双向绑定机制和观察机制,而是通过传统的代理回调或通知将UI事件传给ViewController;
- ViewController只负责将ViewModel装配给View,接受UI事件。
这样做的优点是:
- View可以完全解耦,只要确定好ViewModel和回调接口;
- ViewController可以尽可能少的和View的具体实现打交道,将这部分职责转给了ViewModel,降低了ViewController的负担;
- 使用传统的代理回调和通知方式,学习成本低,降低了维护和调试的成本。
3.2ViewController和Model
- 将获取数据和处理数据的职责从传统MVVM的VM中抽离出来,成为DataController;
- VC请求数据或修改数据的UI事件传递给DC来处理;
- DC接收到数据请求后,向DataRequest获取数据或修改数据,并使用Model包装,返回给VC。
这样做的优点是:
- 避免了传统MVVM的VM过于臃肿的问题,另外模块的职责更清晰;
- 业务逻辑解耦,数据的加工和处理放在DC,VC不关心数据的获取和加工;DC不关心页面的展示和交互;
3.3完整的架构图
3.3.1优点
- 层次清晰,职责明确:和界面有关的逻辑完全划到 ViewModel 和 View 一遍,其中 ViewModel 负责界面相关逻辑,View 负责绘制;DataController 负责页面相关的数据逻辑,而 Model 还是负责纯粹的数据层逻辑。 ViewController 仅仅只是充当简单的胶水作用。
- 耦合度低,测试性高:除开 ViewController 外,各个部件可以说是完全解耦合的,各个部分也是可以完全独立测试的。同一个功能,可以分别由不同的开发人员分别进行开发界面和逻辑,只需要确立好接口即可。
- 复用性高:解耦合带来的额外好处就是复用性高,数据逻辑代码不放在 ViewController 层可以更方便的复用。
- 学习成本低: 本质上来说,这个架构属于对 MVC 的优化,主要在于解决 Massive View Controller 问题,把原本属于 View Controller 的职责根据界面和逻辑部分相应的拆到 ViewModel 和 DataController 当中,所以是一个非常易于理解的架构设计,即使是新手也可以很快上手。
- 开发成本低: 完全不需要引入任何第三方库就可以进行开发,也避免了因为 MVVM 维护成本高的问题。
- 实施性高,重构成本低:可以在 MVC 架构上逐步重构的架构,不需要整体重写,是一种和 MVC 兼容的设计。
3.3.2缺点
- 当页面的交互逻辑非常多时,需要频繁的在 DC-VC-VM 里来回传递信息,造成了大量胶水代码。
- 另外,由于在传统的 MVVM 中 VM 原本是一体的,一些复杂的交互本来可以在 VM 中直接完成测试,如今却需要同时使用 DC 和 VM 并附上一些胶水代码才能进行测试。
- 没有了 Binding,代码写起来会更费劲一点。
3.3.3代码
3.4参考资料
4.总结
每个业务的需求不一样,难度和业务场景都不一样,因此在选择业务组件架构的时候,个人认为应该是根据需要进行合理的安排业务组件的架构。原则上如果业务很简单,那么使用MVC即可,毕竟MVC架构层次清晰,类结构也简单,对于业务复杂度较低的,没必要使用MVVM或者更深层次的架构,来人为的增加复杂度。
这里给一个参考标准:如果使用MVC架构,Controller的代码行数超过500行,那么可以考虑使用MVVM或者其他架构来分化Controller的部分职责。