前言
MVVM设计模式自2005年被微软提出以来,在应用开发领域获得了长久的发展。iOS开发过程中,Apple推荐的MVC模式,因其广泛诟病的massive viewcontroller,逐渐被解耦更充分的MVVM替代。
本质来说,MVVM跟MVC同属于MV(X)类型的模式,是解决开发中model层与UI层解耦的方案。MVVM的优点在于,通过架设viewmodel层(提供连接model和view层的中间件),解决了model和view在MVC controller(MVC中的C)的耦合。
MVVM的原理和优点不在赘述,参见概念图:
Problem
考虑上图的左侧(view与viewmodel解耦):在实际使用过程中,通过viewmodel与view层状态量绑定,很好的解决了view层耦合,解放了viewcontroller,使其更专注于view的组合和管理。同时,viewmodel对model的更新和监听,转化为view层状态量的处理,使我们可以在viewmodel专注于view层页面量数据更新。
再考虑上图右侧:我们通常习惯于在viewmodel层来处理model数据,随着业务的增多,viewmodel中充斥了接口的调用、model的转化、数据的拼装,再加上对view层页面量的转化,viewmodel同样面临着massive化的问题。
另外,viewmodel处理的model层数据越多,也代表着viewmodel与model的耦合度不断上升。某些数据可能会涉及到session维护,即数据也有着生命周期的管理,例如TCP长连的推送,历史数据和当前数据管理等等。那么对于这些可独立的操作,这一块model层数据处理,也具有复用的需求,不能够单单由viewmodel层来处理。
所以说,viewmodel massive化的问题,需要考虑到与model层更好的解耦。
Thinking
结合上图MVVM的定义,viewmodel与view层的关系,是通过状态量的绑定,实现data和action的binding;viewmodel与model层的关系,是更新和监听。
如果viewmodel不直接处理model的获取和转化,而是将复杂的model处理独立出去,然后采取类似viewmodel与view层binding的方式,是否可以实现viewmodel与model的更进一步解耦?
Solution
实现viewmodel与model进一步解耦,一个方案是,在中间架设datacontroller层。如图:
- viewmodel拥有datacontroller,通过对datacontroller的data binding,监听处理过的data,并将data处理为view层页面量;
- datacontroller拥有model,通过接收viewmodel发出的action,对model进行update,同时监听model数据变化,对model数据进行维护、转化和处理;
- view层拥有viewmodel,通过对viewmodel的binding,完成view层页面量的刷新。
notice
- viewmodel与datacontroller需有明确的分工。datacontroller的架设,是为了解耦model层数据处理逻辑,是以model层业务功能为导向的,viewmodel中涉及view层数据的处理,不应归并到datacontroller来处理,而是viewmodel层配合datacontroller来处理数据。
- viewmodel和datacontroller虽是拥有关系,但并非强关联。datacontroller同样可以被其他viewmodel使用,是独立的数据处理组件。
- 理解几个data:viewmodel里的data,是页面状态,面向页面显示的数据;datacontroller里的data,是model数据经过处理后的,面向业务的数据;model里的data,是面向接口的数据层数据。
scene
以行情类APP中的个股详情界面为例。个股详情界面,包含股票的盘口信息,分时走势图,K线图,成交明细,买卖五档等多个功能,这些数据的处理是非常复杂的。同一个功能的数据,可能需要多个接口返回进行拼装。以K线图为例,会要求历史数据和当日数据拼装,并通过TCP实时接收服务器推送的最新一根数据。
考察此时datacontroller,需要完成以下几个功能:
- 多个接口的请求管理。历史数据接口、当日数据接口,以及服务器主动推送。
- session管理。管理TCP推送,以及如果推送断掉后的主动定时刷新。
- model层数据处理。将各个接口返回数据,转化为业务需求数据,例如K线数据拼装、校准、容错。
- 数据源切换。行情数据服务可能存在多个行情源,例如普通行情服务和level2服务,行情源不同。需要datacontroller完成数据源切换,以及对应数据model的转换。输出到viewmodel层的data保持统一。
从以上场景可看出,如果没有datacontroller层,大量数据处理会耦合到viewmodel,一方面造成viewmodel的难以维护,另一方面很多处理逻辑是可独立可复用可单元测试的。
discussion
- 结合flux(react)的数据驱动模式
datacontroller层的架设,实际上也是数据驱动的一种实践。viewmodel通过监听datacontroller,完成数据驱动,最终更新页面状态。
更进一步,可以尝试结合flux(react)思想,设计datacontroller的具体实现。目前我的项目中,通过将flux思想引入datacontroller层,将各个接口实现转换为对应的action和reducer,采用组合的设计模式,自由组合各接口调用。接口返回的数据,区分为业务state,viewmodel通过监听这些state,完成数据层的监听。
react是前端开发的主流框架之一,然而对APP开发来说,由于view更新机制的不同,react state更新的方式并不能很好的体现APP开发的性能。但在数据层,react单向数据流的思想还是值得借鉴,特别是对于业务数据复杂的功能,能够较好地管理数据更新。 -
datacontroller与viewmodel同级的设计
很早之前有一种MVVM设计,datacontroller层与viewmodel层同级,同为viewcontroller拥有。如下图:
这种设计更体现出datacontroller层的独立性。但正如作者所说,频繁的在 DC-VC-VM 里来回传递信息,会造成了大量胶水代码。且datacontroller直接暴露到viewcontroller,不利于view层与model层解耦。