最近团队又在撕逼iOS 项目的架构选型问题,虽然最后MVVM携易测试和易使用的优势取胜,总觉得这个话题意犹未尽,值得再聊一聊。
在进入主题之前,我们先来看看架构的目标,
架构的目标是解决利益相关者的关注点, 让系统达到易理解,易维护,易扩展,易测试,成本可控等特点。
在设计演化架构的过程中,对于一些广为人知的原则(Best Practice)要结合考虑, 如:
- 单一职责原则:一个实体应尽量少地与其他实体之间发生相互作用,应该使得功能模块相对独立,做到低耦合,高内聚。
- 开闭原则:是指对扩展开放,对修改封闭。程序应该具有良好的扩展性,主要依靠接口和抽象类,使程序易于维护和升级。
- 接口隔离原则:使用多个隔离的接口,比使用单个庞大的接口要好。有利于降低耦合,易于维护升级。
常见的还有组合复用原则,最少知识原则, 里氏替换原则等。
一般移动应用中,都包括View 和 Model 组件,由于View 和 Model 之间高度耦合,禁止直接相互操作,需要通过Controller、ViewModel、Presenter等方式桥接,也就带来了多种多样的架构设计。
历史
MVC最初在Smalltalk语言中被发明出来,后来者苹果公司的Cocoa中广泛使用并提出了Apple MVC框架。 MVC的实践者还有WebObjects, Ruby on Rails, �和 Django等。
Martin Fowler 在2004年提出了�MVP作为MVC的替换者,Presentation Model和视图控制无依赖,并呈现应用的状态和视图的行为。
MVVM是在MVP的基础上添加了数据绑定的概念,让数据和视图同步更加简单。
Apple MVC
MVC作为苹果在iOS开发上前期主推的架构,结构简单,容易上手, 前期开发效率高。
让View的归View,Model的归Model,其他的Controller一肩挑起。
注意View 和 Model这里都是很薄的,Model类似于Java中的POJO对象, 只定义属性和Setter/Getter方法。
但由于没有清晰描述业务逻辑,网络请求,数据转换等代码的位置,�容易引起很多问题:
- 大量业务逻辑堆砌在Controller组件内,导致代码臃肿,难以维护,无论添加和修改代码,都让后来者难以下手。
- 逻辑代码耦合度低,相互纠缠,单元测试难以开展。
- 开发者习惯不一,增加团队成员之间代码阅读的复杂度。
�当然也可以在MVC的基础上,通过单一职责和接口隔离原则,把非生命周期和胶水行代码移出Controller,但这已经超出了MVC架构的范围,这里不做讨论。
MVVM
和MVC对比,MVVM解决了Controller臃肿的问题,将逻辑代码、网络请求等都写入了ViewModel中,但需注意ViewModel过于臃肿的问题。
ViewModel为指定的View服务,ViewModel中包含了所有的展示逻辑, 且不依赖于View,让测试更容易进行。
MVVM别名 Model–View–Binder,数据绑定可以让开发者摆脱写View和Model之间业务状态同步代码的繁重工作。
MVVM是随着移动应用开发不停发展后出现的,是最流行/成功的架构, Google在2015年 I/O 大会上介绍给广大开发者的,并且在Android SDK层面上给予了支持,iOS开发者敞开怀抱支持吧。
多注意上图中的Repository 和 LiveData组件。
有兴趣的可以看看Google的官方例子,在iOS开发中可以参考借鉴。
MVP
对比MVVM 和 MVP 的架构图,可以看出两者颇为类似。在两者中View不直接使用Model,它们之间的通信是通过Presenter/ViewModel 来进行的,所有的交互都发生在内部,
但上图中的View是被动的(Passive),Presenter与具体的View是没有直接关联的,而是通过定义具体的接口进行交互,从而在变更View时候可以保持Presenter的不变,达到Presenter重用。
由于通过接口进行交互,我们可以轻松模拟View的事件,从而实现对Presenter单元测试,这也是接口隔离带来的福利, 当然冗余的代码量也是不可避免的。
Redux - Like
熟悉JS的人对 Redux 应该不会陌生,照搬了Stores, Reducers和 Actions等专有名词,模式上照搬了Redux的设计思路。
一个iOS应用本质上就是一个状态机,例如:从一个状态的视图由用户事件Action或者网络请求返回的数据Action触发达到下一个状态的视图。
细心地童鞋可能发现,上图中的数据流是单向的,是由数据驱动的。
更多参看ReSwift 开源项目和样例
VIPER
从架构图中可以看出VIPER比MPV更进一步,把层次分的更加明确,模块功能更单一(单一责任原则),更加方便测试。
- Interaction 关于数据和网络请求的业务逻辑,例如从服务器/本地缓存中获取一些数据。
- Entity 就是普通的数据对象,轻Model。
- Router处理一个视图到另一个视图的导航,需要初始化所有其他类。
这里可以思考一下:为什么Router 在VIPER里是必要的,而在MVVM里不是必须的?
最后
架构本无好坏高下之分,只是适应了不同的场景孕育而生,是解决问题的工具,其实很多项目中都存在不同架构的混合使用的现象。
在实际项目中,莫要盲目追求‘高级’的架构,也不存在一个架构可以解决所有问题,结合实际需求和团队情况,选择适合自己的框架,逐步升级迭代,才能降低项目失败的风险。
更多
MVP WIKI
MVVM WIKI
请关注豆志昂扬微信公众号获取更多内容:
- 扫描下图二维码;
- 直接添加公众号豆志昂扬;