架构模式
在构建iOS应用程序时,测试并不总是我们的主要工作。而当我们在移动开发中想要丰富测试经验时,我们发现给iOS 应用写测试代码有一定困难——即使我们遵循了Apple的指导原则并且实现了其 MVC
模式。为了更好地测试我们必须想办法用更好的方式去构建我们的iOS应用程序。那么个好的架构应该具备哪些特点呢?
- 各个实体分工明确,任务量分配适中
- 可测试性(当然做好第一点,第二点往往就具备了)
- 易用性和低成本维护
既然我们定义了好的架构模式的特点,下面我们来一一回顾一下那些常用的架构模式是否符合我们理想的iOS应用程序构建模式。
MVC模式:
MVC
即Model-VIew-Controller
。MVC
模式致力于关注点的切分,这意味着model
和controller
的逻辑是不与用户界面(View
)挂钩的。Model层代表了描述业务逻辑和数据的一系列类的集合。它也定义了数据修改和操作的业务规则。View代表了UI
组件,像UIView
,UIButton
,UITableView
等。他只负责展示从controller
接收到的数据。因此,维护和测试程序变得更加简单容易。然而在Cocoa
的MVC模式中Controller
经常被混杂在View
的生命周期中,因此很难说View
和ViewController
是分离的,这也往往驱使人们写出臃肿的视图控制器,尽管仍可以将业务逻辑和数据转换到Model
,但是大多数情况下当需要为View
减负的时候我们却无能为力了。View
的最大的任务就是向Controller
传递用户动作事件。ViewController
不再承担一切代理和数据源的职责,通常只负责一些分发和取消网络请求以及一些其他的任务。当在进行单元测试的时候你会发现问题越来越明显。因为你的ViewController
和View
是紧密耦合的,对它们进行测试就显得很艰难。Cocoa MVC
看来并不是我们定义中理想的架构模式。
MVP模式:
这个模式把Presenter
换成Controller
就和MVC
非常相像了。这个设计模式把应用程序分成了3个主要方面:Model
、View
和Presenter
,其中的Model
和View
与MVC
模中的角色相同。Presenter
负责处理View
背后所有的UI事件。它通过View
接收用户输入,之后利用Model
来处理用户的数据,最后把结果返回给View
。与View
和Controller
不同,View
和Presenter
之间是完全解耦的,他们通过接口来交互。另外,presenter
不像controller
处理进入的请求。这不是正解决了Cocoa MVC
中ViewController
和View
的耦合问题吗?就MVP
模式而言,UIViewController
的子类实际上就是Views
并不是Presenters
。这点区别使得这种模式的可测试性得到了极大的提高,付出的代价是开发速度的一些降低,因为必须要做一些手动的数据和事件绑定。但是这也意味着我们将最主要的任务划分到Presenter
和Model
,而View
的功能较少——各个实体任务量分配不均衡。
MVVM模式:
即Model-View-View Model
。这个模式提供对View
和View Model
的双向数据绑定。这使得View Model
的状态改变可以自动传递给View
。典型的情况是,View Model
通过使用obsever
模式(观察者模式)来将View Model
的变化通知给model
。View Model
负责暴漏方法,命令,其他属性来操作VIew
的状态,组装model
作为View
动作的结果,并且触发View
自己的事件。它和MVP
模式看起来非常像:MVVM
将ViewController
视作View
,View
和Model
之间没有紧密的联系。在使用MVVM
模式时,自然而然会想到ReactiveCoca
,反之亦然。尽管通过简单的绑定来使用MVVM
是可实现的,但是ReactiveCocoa
却能更好的发挥MVVM
模式的特点。但是使用这个框架有个难以忽略的事实:当你刚开始使用ReactiveCoca
的时候有很大的可能就会把事情搞砸。换句话来说就是,如果发现了一些错误,当你试图查看函数调用栈时你可能会喊:“天哪,好深的函数调用栈“!调试出这个bug
可能会花费大量的时间。MVVM
很诱人,因为它集合了上述方法的优点,并且由于在View
层的绑定,它并不需要其他附加的代码来更新View
,尽管这样,可测试性依然很强——符合我们理想中好架构模式的定义。
Viper架构模式:
由视图 (View
),交互器 (Interactor
),展示器 (Presenter
),实体 (Entity
) 以及路由 (Routing
) 组成。
视图:根据展示器的要求显示界面,并将用户输入反馈给展示器。
交互器:包含由用例指定的业务逻辑。
展示器:包含为显示(从交互器接受的内容)做的准备工作的相关视图逻辑,并对用户输入进行反馈(从交互器获取新数据)。
实体:包含交互器要使用的基本模型对象。
路由:包含用来描述屏幕显示和显示顺序的导航逻辑。
Viper
将应用程序的逻辑结构划分为不同的责任层。这使得它更容易隔离依赖项 (如数据库),也更容易测试各层间的边界处的交互。
Viper
的不同层提供了明确的程序逻辑以及导航控制代码来避免视图控制器太过于臃肿的问题,利用 Viper
,视图控制器可以简洁高效,意义明确地控制视图。视图控制器中代码和所有的其他类很容易理解,容易测试,理所当然也更易维护。
毫无疑问,Viper
在划分责任的粒度上比以上几种模式都要优秀,自然而然就有更好的可测试性,当然你必须为很小功能的类写出大量的接口。如果是在大型项目中使用Viper
,Viper
架构模式符合我们理想中好架构模式的定义。
从以上几种架构模式的分析中我们可以知道:没有哪种架构模式是绝对好的,所以选择架构模式是一个根据实际情况具体分析利弊的过程。