被神化的MVVM和被错误认识的Model层

2018/8/11 posted in  iOS

MVVM是一种双向绑定技术,使用VM实现数据的变化更新视图,视图的变化更新数据的处理,整个过程是一种数据绑定的过程。
网络上可以看到大多数人吹捧MVVM,但是千奇百怪的,并没有人能很好的理解MVVM,反而使代码变得异常复杂。
MVC的关系图

V层->C层:事件
Note right of V层:特殊情况下可以持有M层
C层->V层:创建/更新
C层->M层: 调用
M层->C层: 通知
M V C
Model模型 View视图 Controller控制器

1.被误解的MVC

最开始实行的一套机制是MVC 但是很多业务代码被放在了C层处理,导致出现了一个C中出现了上千行代码,给调试带来了很多影响。

后来才试着把JSON数据解析成数据模型,这样把一部分代码挪到了M模型层,但是这个模型是很简单的,只是单纯的属性定义。

这样显然是不够的,很多业务代码还是存在C层当中,并没有对C很好的进行瘦身,这样的MVC框架太过臃肿。

而其实真正的MVC框架中的M层不应该只是简单的数据模型,而应该是业务模型,也就是所有业务数据和业务实现逻辑应该定义在M层,而且应该和具体的界面没有关系,可以独立存在,甚至可以编译出静态库给第三方使用,就像我们下面将要说到的ViewModel层相似。

2.引出ViewModel层

其实大多时候界面和业务逻辑是绑在一起的,这个界面的展示要调用某个业务逻辑,完成之后直接更新界面,大多时候C只是多余的,所以才把这部分的逻辑代码(网络请求,数据库操作,解析,业务逻辑)抽象出一个单独的类,这个类叫VM类。

V层->VM层:
VM层->V层:
M层->VM层:
VM层->M层:
C层->VM层:

当前的VM层负责视图、模型、C层的三个部分的交互和耦合。

下一步考虑是不是可以把C层的事件处理也拿出来,然后就用到了RAC,负责事件的处理和传递。
这样的话就可以达到给C层减肥的效果了。

但是并不是说C层就没有用了。

在MVVM中的M只是一个简单的饿数据模型的概念,如果把它当做一层,显然不符合横向切分的规则,所以MVVM中的M就是一个伪概念。

3.MVVM的过于神化

同时MVVM当中的VM是严格和View绑定的,这是一种强耦合,但是实际情况下界面可能会随着升级而变化,往往业务逻辑是相对稳定的,所以上面的这种以VM为中心的设计模式很不清爽,所以MVVM当中的VM层也是一个
伪概念。

然后是View事件处理。C层是一个负责调度和控制的模块,他的作用就是处理视图的事件,然后调用业务逻辑,然后接收业务逻辑的处理结果通知,然后再通知视图去刷新界面,这就是C层存在的意义。
而RAC的出现则将这部分的处理给活生生的代替掉了。也就是通过RAC所谓的响应式和触发式这种机制就能实现将事件的调度处理放在任何地方任何时候都能完成。这样做的目的使得我们可以分散和分解代码。但结果出现的问题呢?就是同一个单元调度处理逻辑和功能的构建完全放在了一个地方,但不同的单元逻辑的又分散在不同的地方,无法去分类统一管理和维护。因此你无法一下子就知道某个功能所有调度到底是如何实现以及在哪里实现的。因为RAC将功能构建和事件处理完全粘合到一个大的函数体内部,并且是代码套代码的模式,这种方式严重的破坏了面向对象里面的构建和处理分离的设计模式理论。更麻烦的是其高昂的学习和维护成本,代码阅读理解困难,以及无处不在的闭包使用。试想一下这个对于一个初学者来说是不是噩梦?,一旦出了问题对于维护和代码调试是不是噩梦?而且使用不当就会出现循环引用的严重问题。这样一来原本C层一个调度总管的职责被RAC来接管后,这些处理将变得分散和无序,当我们要做一些统一的管理比如HOOK和AOP方面的东西时就变得无法下手了。 不可否认的是RAC在处理连续调用以及顺序响应方面有一定的优势。一个例子是我们可能有连续的多个跟服务器的网络请求,这时候用RAC进行这种处理能方便的解决问题。但是我想说的是当存在这种场景时,我们更加应该将这种连续的网络调用在M层内部消化掉,而只给C层提供一个简易而方便的接口,让C层根本不需要关心这种调用的连续性。因此可以说为了把C层的代码给消化掉而引入RAC的机制,不仅没有简化掉系统反而降低了系统的可维护性和可读性。RAC机制根本就不适合用在事件处理中。优秀的应用和框架并不在代码的多寡,而是整体系统的代码简单易读,各部分职责分明,容易维护的调试。

其实这一切的一切都来源于对MVC当中Model的错误认识和理解。

4.正确理解MVC

M层不依赖C层和V层独立存在,C层负责关联二者,V层只负责展示,M层持有数据和业务的具体实现,而C层则处理事件响应以及业务的调用以及通知界面更新。三者之间一定要明确的定义为单向依赖,而不应该出现双向依赖。下面是三层的依赖关系图:

M层->C层:被C层依赖
Note right of M层:M:持有数据和业务的具体实现
C层->V层:依赖V层
Note left of V层:V:展示
Note left of C层:C:关联M和V&事件响应&业务调用&界面更新。