优秀的 iOS 应用架构:MVVM、MVC、VIPER,孰优孰劣?
内容非常丰富的一篇讲应用架构的文章,花了挺多时间来看,还没看的很懂。
什么是好的软件架构
each object to have a specific, clear role
这句话我的理解是,每个对象拥有自己的特有的,清晰的定位。
这样的好处的是,每个对象之间职责清晰明确,模糊的边界几乎不存在。便于阅读,维护,组装。
同时,单向数据流也是被推崇的。单项数据流可以轻松的把握数据传递的路径,避免混乱的边界问题。
测试
topic这里讲述的方式是通过TDD。
这点国内和国外的最大不同在于,国外注重测试点,必须通过测试。
国内基本没有自动化的测试用例,依赖人工测试,快速迭代。
TDD不适用于敏捷开发,测试用例变动太大。
此外,由于大量业务逻辑混杂和耦合,无法明确边界,无法便于测试。
topic这里强调,如果开发者认为测试用例难以编写,说明架构已经出现了很大的问题(这里的确是)
什么是坏架构
第一点,每个执行文件的代码行数。
topic举了个例子,如果一个swift的viewcontroller行数多于3000行,或者专用的测试超过4000,这就是一个明显的信号。
global states and Up Delegate,这两个也是topic中提到的问题。
全局变量最大的问题我们无法明确值在哪里被改变了。
up delegate(代理提升?)这个概念真的不清楚。
up delegate 会造成大量class关联在一起。
设计模式
SOLID principle 当然是最经典的。但是topic这里没有讲这个,而是一点人生经验。
topic这里提到,设计模式是为了解决一类问题的方法定式。多数人沉迷某一种模式,认为是万金油,其实这是不对。离开了特定的问题,设计模式是没有用处的。这里提到了单例,我认同这种观点。单例本身不是一个坏的设计模式,而是滥用造成了单例泛滥。单例的目的是为了解决资源竞争的问题。或者通过通用设定去访问数据库或者某些接口。而在ios里,许多使用单例仅仅是为了全局同步。
logger日志是一个很好的例子,为了打印一个日志,我们不必要将logger传入每一个类中。
即使用了单例,我们也不一定要在接口中暴露出来,尤其是swift。
Composition组合模式
topic认为,如果有一种模式值得推荐,那么就是组合模式。因为topic认为,如果遵循组合模式的准则,那么一切部分都是遵循single responsibility 单一职责
遵循组合模式可以很容易的去注入,方便测试。
unity引擎是一个很好的例子。
另外一种很好的模式是依赖注入(这里存疑,我不认为依赖注入是一种很好的模式)
topic认为,小的对象,为何需要硬编码,只需通过依赖注入而不需要专门创建接口。
流行架构模式
MVC
这里的MVC是传统的MVC。传统MVC适合WEB(传统的web)。
在apple的mvc中。viewcontroller承担者mediator这和controller的职责,和view的生命周期绑定非常紧密,因此vc非常容易变得非常臃肿。因为vc无法复用,因此view也极其难复用。
在vc中,会执行网络下载,数据处理,数据绑定,业务逻辑,因此非常非常难写测试用例。因此,绝大多数的vc是没有测试 用例的
Viper
相对于viper,MVC/APPLE MVC更像是UI 模式。viper是第一个考虑如何解决整个设计架构的。
在viper中,interactor负责业务逻辑,组合不同service。 Router负责管理跳转逻辑,管理uikit framework。Presenter负责和uiKit 无关的展示逻辑。
viper很棒,但是需要非常多的模板代码,因此很少人使用。
MVVM
topic认为,MVVM是现在最适合ios的架构。
MVVM可以非常容易去测试view model。view model 不包含UI kit,因此和view controller的生命周期无关。
不受用FRP(函数变成)也可以创建完整的MVVM模式,但是使用使用起来会更加好。
MVC只能完成30%的测试覆盖的话,MVVM可以提高到70%甚至更高。
虽然MVVM是最流行的架构之一,但是很多人误解了她的用法。MVVM的优势在于便于测试,可以不依赖view controller的生命周期来测试view model中的业务逻辑。
唯一无法解决的测试问题在于,VC生命周期中的绑定。
这里举了一个例子。
有一个view controller A,拥有一个依赖,这时候需要添加另外一个view controller,这个B需要观察数据,这个时候,A就需要拥有B的依赖了。如果这时候还需要添加其他的view controller,那么A就变成一大堆的view controller的池子(这比喻很有意思)
因此,MVVM中大量的优点被这个问题掩盖掉了。
怎么解决呢?
事实上,我们希望不相关的view model之间互不依赖。为了更简单的去测试VM,topic提出了MVVM+。MVVM+意味着 MVVM with flow coordinator。这里,topic提到了redux。
通过flow coordinator,将view controller作为一个黑盒来使用。例如一个用户设置页面,我们只需要关心save和cancel操作。
Flow Coordinato
Flow Coordinato 是管理view controller的地方,准确的来说是决定展示的内容的地方。controller不需要去关心是怎么配置的,甚至可以将一个view controller配置到不同的child controller下。
Flow Coordinato可以很好的消灭if
判断
ReSwift
ReSwift是非常好的三方库,使用了redux的实现,非常利于测试。这点,在react的开发中我深刻感受到了。redux的设计非常利于状态的回放。由于状态的更改都是利用纯函数,因此利于测试。
Conclusion(总结)
这个topic真的非常长,提到的内容太多,一时很难消化。
事实上,没有最好的框架,正如topic一开始讲到的。一切框架都是为了解决特定的问题去实现的。i开了具体问题谈论框架是没有意义的。可以看到,viper和MVVM都有自己独特的优点。在可测试性上都有很强的提升。但是,viper大量的模板代码和MVVM难以解决的vc bind的问题,让很多开发者望而却步。