03--(一)面向协议的MVVM

2021-09-16  本文已影响0人  修_远

一、什么是MVVM

MVVM是从js中率先被使用的模式。在js框架的支持下,可以非常便捷的实现MVVM,例如与:v-bind、ng-bind等,都是非常便捷即可完成MVVM中视图和数据的绑定。但是,在原生中,rac、rxswift都是基于原生框架而开发的一个三方库,相对于vue、angular这类直接作为应用开发框架的架子来说,并没有那么不可或缺。说直白点,没有vue、或者vue没有提供相关的功能,我们可以理直气壮的说系统不支持,但对于rac和rxswift,如果存在未支持的地方,我们只能去探索源码,然后再去想办法解决。所以,从某种程度来讲,引入这类三方库可能并没有减轻我们的工作量,甚至有增大风险的可能,譬如说某个crash定位到了三方库中,你就不得不花大量的时间去研究其源码。当然,我这里并不是说这两个库不好,恰恰相反,这两个库是非常值得iOS开发者去拜读的,其中响应式的设计理念以及对UIKit的支持都是非常厉害的,值得我们花时间去研究。

如果你把MVVM当做是某种设计模式,那么在使用中难免会出现生搬硬套的现象。在这篇文章中,我更愿意将MVVM当做是一种思想、一种概念,并不是某个具体的模板。其实,MVC、MVP、MVVM、VIPER等主流模式,都可以看做是MVC的扩展,将职责划分得更加细腻。所以它们的核心思想都是“划分之责”和各个职责之间的“通信”。响应式框架rac和rxswift帮我们完成了不同职责之间的“通信”,但代价是要引入这个库。

从设计模式的角度看,MVVM表示一种固定的模式。以某种特定的方式,订阅View/ViewModel中的变化源信号,另一端处理这个信号。当信号源发生变化时,会通知处理者去处理。响应式框架RAC确实能帮我们完成这个过程,但其弊端也有说过。

二、 什么是协议

Protocol是一种抽象的数据结构,只定义行为,可以为类扩展某种能力,不能脱离类而独立存在。因为协议仅仅是定义行为,本身是没有任何依赖,需要遵循该协议的类会实现该行为,这部分实现代码往往是我们说的业务代码,也是这个类本身的特点。但对外而言,我们可能只关心一个类是否有遵循某个协议,而不需要直接依赖该类,通过了解类所有的行为才能决议类是否拥有某个行为。Protocol还可以继承、组合,提供开发者更多便利的选择。对Protocol的依赖也是非常好复用、分解的。

三、为什么需要MVVM

从项目架构的角度看,MVVM表示设计思想。如何将职责进行分类,以及每个职责之间的如何交互。我们可以按下面的职责对每一层进行分类。一个复杂的视图往往会有多个、多层子视图关系,而这些视图会以树形结构呈现。比较好的设计是:“一个View对应一个ViewModel”,通常情况下,我们会按照View的结构,构造一个相同结构的ViewModel层,这样的写法,在代码量上肯定会有所增加,但带来的好处肯定也不少。从“大道至简”的思想来看,保证一个类尽量简单、业务尽量单一,这样的可读、复用、可替换性往往会更高。从职责划分的角度来看,这样的写法更加能表明一个类的作用,“View”只做了两件事,其一、给SuperView提供视图渲染,只需要调用addSubview方法添加到父视图上即可,其二、提供交互能力给到ViewModel并展示ViewModel中的数据。通过对协议的依赖,View和ViewModel之间并不用互相知道对方,两者只需要分别按照给定的协议调用即可完成相互之间的通信,关于如何处理异步消息,后面章节中会讲。从结构上来看,一个View在App只与其SuperView和ViewModel有关,当我们需要复用这个View的时候,只需要更改其SuperView和ViewModel即可,当我们需要替换这个View时(可能是相同的业务逻辑,不同的展示方式),只需要实现指定的协议即可。上面这些规则更多是假设的场景,实际开发中面临着会是更加复杂的业务逻辑,比如两个子View之间可能某种交互,两个子ViewModel之间也存在某种交互,如果以Controller作为核心控制器来管理这些逻辑,会让Controller中有超级多的代理方法,并在代理方法中做一个非常长的转发链路。如果不以Controller作转发,让两个类之间相互依赖,往往会导致非常复杂的类的依赖关系,最后会让业务代码变得非常复杂。目前比较普遍的做法是依赖响应式框架RAC来实现这个过程,但对一个三方库的过度依赖往往是不太好的设计,而且RAC要求对类的特征的修改,只能定义RAC中提供的signal,才能发起订阅。这篇文章介绍如何借助runtime的机制,使用系统提供的最原始的途径来构建复杂的交互场景。下面是面向协议的MVVM中常见的几个职责,其实还是存在一些比较复杂的依赖,比如Controller对ViewModel的依赖,ViewModel对数据的耦合,所以我们需要把它们分得更细致,后面的文章中会介绍。

四、MVVM的基础结构

  1. 初始化:以Controller为核心,可以构建树的结构,左子树表示View的视图层级,右子树表示ViewModel的相关层级
01-初始化.png
  1. 设置代理:指定协议、设置代理。完成这一步,即完成了将View的事件传递给指定的ViewModel的过程。
02-设置代理.png
  1. 兄弟通信:在上面这种结构中,如果两个子ViewModel之间需要通信,必须借助父ViewModel。
03-兄弟通信.png

【说明】每一个红色的箭头都需要建立一个代理(这里不考虑block等其他方式)
通过父ViewModel将消息传递到另一个子ViewModel中

  1. 孙子通信:如果层级更深,则需要建立更多的代理链,让一个类中增加了非常多与自己业务完全无关的代码。
04-孙子通信.png 05-逐级回调.png

上面以完全二叉树为例,介绍了View、ViewModel之间的关系。实际开发中,在处理各节点之间的通信是需要付出非常大的代价的,方式往往就两种:

  1. 代理(block)逐级回调给最近公共双亲节点,然后再逐级调用到消息接受的节点;
  2. 通知满天飞;

所以只要有效解决了各个节点之间:一对一、一对多、多对一各种情况的通信,MVVM就会是一个非常便于阅读的架构。

上一篇下一篇

猜你喜欢

热点阅读