搜集的架构思想资料,供大家互相学习
当前业界已有一些常用的方案:MVC、MVP、 MVVM、VIPER等。
组件化目标,体现在架构层面主要是职责均摊、可测试性、易用性与低维护成本。
Apple’s MVC
苹果在探索开发框架时给出了自己的MVC探索,其中对传统的MVC进行了分析并提出了自己的MVC,然而在实际视线中,演变出了<理想>和<现实>两个版本。
传统的MVC
苹果认为,在传统的MVC中,各部分之间时按上图的方式协作工作的:用户与视图发生交互,View对象将交互时间封装成event对象传递给Controller对象。Controller对象接收到event对象之后对其进行解析加工,然后按照解析结果对View对象发送请求去更新显示,或对Modeld对象发送请求来更新其状态。而当Model对象发生状态变化,其会通知已注册的观察者作出相应的响应。
尽管运用一些绑定技术,也可以实现以上传统的MVC模式,但苹果还是认为View对象和Model对象在应用程序中应该尽可能的保持独立来保证崇勇,因此,出现了Apple’s MVC。
理想中的 Apple’s MVC
在 Apple’s MVC中,Model对象状态的变化是通过Controller对象来传递给View对象的。Controller对象在这个设计中融合了Mediator模式和Strategy模式,作为View对象和Model对象之间的数据流动的调节器。同事,View对象通过 target-action
机制实现了Command模式与Controller进行交互。
现实中的 Apple’s MVC
然而在实际的开发当中,Apple’s MVC模式驱使人们写出臃肿的ViewController,因为它们维护者View的生命周期,View和ViewController是紧密联系的,View的一些维护逻辑很自然的就出现在了ViewController中。ViewController最终会承担一切delegate和dataSource的指责,还负责一些分发和取消网络请求以及一些其他的任务。
由于缺乏约束,我们很容易写出这样的代码:
XXTableViewCell *cell = [tableView dequeueResuseableCellWithIdentifier:@"cell" atIndexPath:index];
[cell configureCellWithUser:user];
这个cell是由View直接来调用Model,所以事实上MVC的原则已经违背了,但是这种情况是一直发生的神祗于人们不觉得这里有哪些不对。如果严格遵守MVC的话,你回把对cell的设置放在Controller中,不向View传递一个Model对象,然而这样又会大大增加Controller的体积。
小结
- 职责均摊 —— View和Model确实是分开的,但是View和Controller缺失紧密耦合的。
- 可测试性 —— 由于糟糕的分散性,只能对Model进行测试。
- 易用性 —— 与其他几种模式相比最小的代码量。熟悉的人很多,因而维护起来也较为容易。
MVP
从上图可以看出,MVP和苹果提出的MVC很像,但后者中View是和Controller是紧密耦合的,而MVP的协调器Presenter并没有对ViewController的生命周期做任何改变,因此View可以很容易的被模拟出来。在Presenter中根本没有和布局有关的代码,但是它却负责更新View的数据和状态。
就MVP而言,UIViewController的子类实际上就是Views而不是Presenters。这点区别使得这种模式的可测试性得到了极大的提高,付出的代价是开发速度的一些降低,因为必须要做一些手动的数据和事件绑定。
MVP是第一个如何协调整合三个实际上分离的层次的架构模式,既然我们不希望View涉及到Model,那么在现实的ViewController(其实就是View)中处理这种协调的逻辑就是不正确的,因此我们需要在其他地方来做这些事情。例如,我们可以做基于整个APP范围内的路由服务,由它来负责执行协调任务,以及View到View的展示。这个出现并且必须处理的问题不仅仅是在MVP模式中,同事也存在于下面的几种方案中。
小结
- 职责均摊 —— 我们将最主要的任务划分到Presenter和Model,而View的功能较少。
- 可测试性 —— 非常阿訇,由于一个功能简单的View层,所以测试大多数业务逻辑也变得简单。
- 易用性 —— 代码量比MVC模式多1倍甚至更多,但同时MVP的概念却非常清晰。
MVVM
从上图可以看出它和MVP模式看起来非常像:
- MVVM将ViewController视作View
- 在View和Model之间没有紧密的联系
在MVVM中,Mediator的角色由ViewModel来担当,ViewModel基本上就是View以及View的状态的非UIKit的表现形式。ViewModel会引起Model的改变同时会将Model的改变更新到自身并且因为我们绑定了View和ViewModel,View也会做出相应的更新。
绑定是支持MVVM的一个重要机制,而实现绑定主要有两种现成的方案:
- 使用基于KVO的绑定库RZDataBinding和SwiftBond
- 使用完全的函数响应式编程,比如像ReactiveCocoa、RxSwift或者PromiseKit
事实上,目前业界在提起MVVM时就会想到ReactiveCocoa,反之亦然。尽管通过简单的绑定来使用MVVM是可实现的,但是ReactiveCocoa却能更好的发挥MVVM的特点。
但是在使用这个框架时会有一个很大的问题,那就是在开发过程中如果发现了一些错误,由于各种绑定、订阅、信号流等机制,调用栈很深而且调用逻辑不直观,调试出这个bug可能会花费大量的时间。
小结
- 职责均摊 —— 在栗子中并不是很清晰,但是事实上,MVVM的View要比MVP中的View承担的责任多。因为前者通过ViewModel的设置绑定来更新状态,而后者只舰艇Presenter的时间但并不会对自己有什么更新。
- 可测试性 —— ViewModel不知道关于View的任何事情,这允许我们可以轻易的测试ViewModel。同时View也可以被测试,但是由于属于UIKit的范畴,对它们的测试通常会被忽略。
- 易用性 —— 在我们例子中的代码量和MVP的差不多,但是在实际开发中,我们必须把View中的事件指向Presenter并且手动的来更新View,如果使用绑定的话,MVVM代码量将会小的多。
VIPER
VIPER与以上所有MV(X)系列有较大的区别,它将营造建筑的经验运用到iOS应用的构建中,遵从职责均摊的原则,它将应用分成了5个部分:
- 视图:根据展示器的要求显示界面,并将用户输入反馈给展示器。
- 交互器: 包含由用例制定的业务逻辑。
- 展示器: 包含为显示(从交互器接受的内容)做的准备工作的相关视图逻辑,并对用户输出进行反馈(从交互器获取新数据)。
- 实体: 包含交互器要使用的基本模型对象。
- 路由: 包含用来描述屏幕显示和显示顺序的导航逻辑。
与MV(X)系列相比,VIPER明确提出了路由的改变,职责划分颗粒度更细。把Entity作为最小的数据结构,数据交互等Model逻辑转移到了Interactor中。MV(X)的Controller/Presenter/ViewModel中的UI展示相关的职责被放在了Presenter中,但其实并不包含数据操作相关的逻辑。
小结
- 职责均摊 —— VIPER的任务划分颗粒度最细,职责最明确。
- 可测试性 —— 更好的分布性就有更好的可测试性。
- 易用性 —— 必须为很小功能的类写出大量的接口,代码量相对前几种架构来说是最大的。