MVVM后,下一代开发模式在哪?

简介: 写在前面讨论下一代开发模式的演化、优化方向和可能,不一定正确希望和感兴趣的读者交流。任何模式的选择一定要根据当时的开发需要来决定。比如:实验性、迭代很快的简单需求,一般会先选择MVC尝试,待明确方向后,再考虑改为MVVM。

写在前面

讨论下一代开发模式的演化、优化方向和可能,不一定正确希望和感兴趣的读者交流。

任何模式的选择一定要根据当时的开发需要来决定。比如:实验性、迭代很快的简单需求,一般会先选择MVC尝试,待明确方向后,再考虑改为MVVM。

背景

首先,看下iOS首推的MVC模式。

  • M:单纯的从网络获取回来的数据模型
  • V:视图界面
  • C:ViewController

image.png

ViewController负责View和Model之间调度,View发生交互事件会通过target-action或者delegate方式回调给ViewController,与此同时ViewController还要承担把Model通过KVO、Notification方式传来的数据传输给View用于展示的责任。

随着业务越来越复杂,视图交互越复杂,导致Controller越来越臃肿,负重前行。福报修多了的结果就是,不行了就重构,重构不了就换掉。


为了解决MVC带来的问题,MVVM出现了。

image.png

把View和Controller都放在了View层(相当于把Controller一部分逻辑抽离了出来),Model层依然是服务端返回的数据模型。而ViewModel充当了一个UI适配器的角色,也就是说View中每个UI元素都应该在ViewModel找到与之对应的属性。除此之外,从Controller抽离出来的与UI有关的逻辑都放在了ViewModel中,这样就减轻了Controller的负担。

从以上的架构图中,我们可以很清晰的梳理出各自的分工。

  • View层:视图展示。包含UIView以及UIViewController,View层是可以持有ViewModel的。
  • ViewModel层:视图适配器。暴露属性与View元素显示内容或者元素状态一一对应。一般情况下ViewModel暴露的属性建议是readOnly的,还有一点,ViewModel层是可以持有Model的。
  • Model层:数据模型与持久化抽象模型。数据模型很好理解,就是从服务器拉回来的JSON数据。而持久化抽象模型暂时放在Model层,是因为MVVM诞生之初就没有对这块进行很细致的描述。按照经验,我们通常把数据库、文件操作封装成Model,并对外提供操作接口。(有些公司把数据存取操作单拎出来一层,称之为DataAdapter层,所以在业内会有很多MVVM的变种,但其本质上都是MVVM)。
  • Binder:MVVM的灵魂。可惜在MVVM这几个英文单词中并没有它的一席之地,它的最主要作用是在View和ViewModel之间做了双向数据绑定。如果MVVM没有Binder,那么它与MVC的差异不是很大。

需要注意一点,View持有ViewModel,ViewModel不能持有View(即ViewModel不能依赖UIKit中任何东西)。有两个原因:一是为了ViewModel可测性,即单元测试方便进行;二是团队人员可分离开发。可总结为:更加干净的解耦。

存在的问题

MVVM公认的问题

  1. 学习成本高
  2. 绑定数据比较繁琐,容易引起crash
  3. 犹豫要不要引入RAC

问题1

任何新技术,新模式都是有学习成本,MVVM也不例外,但是并不是非常高,阅读相关文章,以及技术大咖做的demo,经过一两个需求就基本掌握了。

问题2

在不使用RAC的情况下,一般使用KVO、类KVO来解决,需要在初始时绑定,dealloc移除绑定,还要避免主动修改viewModel值造成UI的循环响应修改。的确,相对繁琐,但已经有其他开源方案可以解决,既方便,又安全。

问题3

(个人意见)如果非必需,建议不要引用RAC,有两个原因,一是因为RAC学习成本需要颠覆之前的开发方式;二是RAC对性能影响非常大,一个简单的数据响应也会生成非常多信号量。

问题不止这些

经过长时间的使用,发现MVVM的viewModel的确可以抽离V/VC中的业务逻辑,但是不同VM由于对外接口不统一,相似的业务,如果通过VM之间的继承,可以解决当时问题,但是随着迭代,判断逻辑越来越多;如果创建各自独立VM,部分能力又会重复造轮子。

例如,页面A某个功能需要,先判断登陆,再上传修改;页面B某个功能需要,先判断登陆,再查看信息。

方案一
viewModelB继承viewModelA,viewModelA声明一个nextActionAfterLoginCheck public方法。viewModelA登录判断后调用nextActionAfterLoginCheck,而viewModelB重写即可。

image.png

但后期需要页面A在登录增加封禁账号不能修改信息,那么页面A就不用调用nextActionAfterLoginCheck,但是页面B需要。这时可以再声明一个-(bool)shouldToDoNextAction 方法,在登录判断后通过这个方法获取A、B各自返回值,决定是否调用nextActionAfterLoginCheck,既保证nextActionAfterLoginCheck业务独立性(不涉及账户相关判断),又保证继承关系不变。但是登录模块代码引入了新的判断,VM臃肿化前兆现象。方案二
页面A、页面B各自拥有自己的viewModel,这样登录逻辑便重复,但是日后修改,两个页面独立,互不影响。

有没有更好的解决方案呢?

分析

对比一下前后功能
* 开始时,页面A、B所包含的功能:登录判断、上传修改、查看信息。
* 后期迭代,页面A增加:封禁用户判断。

表面上,造成VM臃肿隐患的原因,是既要保留公用登录模块的前提下,完成页面A新的业务需要,又要最小影响页面B,则需要增加判断完成。那进一步分析业务链路,发现最终的形态如下:

image.png

根本上,VM自身承担的业务定义、执行,造成了各个功能无法灵活组织,随时可变,每一个功能修改都有可能影响到其他业务。

解决方案

如果VM只承担业务定义呢?通俗一点的说法,V/VC告诉VM要做一个什么业务,VM定义这个业务需要哪几个功能,具体功能实现不在VM实现,那么VM在后期迭代中只需要更新定义即可,无非是增删改定义的几个功能而已。用这个思路分析上面case,既然不用实现功能,那么viewModelB就不用继承viewModelB,两个页面独立,viewModelA业务定义:登录判断、封禁判断、上传修改,viewModelB业务定义:登录判断、查看信息。如此分析,可以发现,每个功能都被拆分成了相对“干净”的最小功能,那么这些功能在哪里实现呢?暂且叫它业务中心(Service)吧,新MVVMS模型图如下:

image.png

技术调研

若要实现上面的方案,必须解决3个问题:

  • 如何定义
  • 结果如何返回
  • Service应该如何设计

需要一套语法解决定义

个人觉得VM最终目标只需要传入一串字符、参数,就能完成一个具体业务。那么定义了字符,就涉及到解析、翻译,借鉴编译原理,我们需要一套定义业务的语法。

将相对独立、闭环的功能,称为原子功能,每个原子功能为一个编码,相关功能为一个模块,一个模块为一个编码区。例如:账户为模块Account,则登录判断(Account01)、登录(Account02)、封禁判断(Account03);信息模块Info,上传修改(Info01)。

功能之间执行先后顺序、串行、并行、是否执行,通过操作符来决定。类似四则运算中,括号()决定执行的顺序,+、-、×、÷决定两侧的运算结果。那么可不可以定义+表示只有左侧执行成功,才执行右侧,-表示左侧执行失败再执行右侧呢?

那么按照上面的case,页面A的业务定义为:

(Account01-Account02)+Account03+Info01。

响应数据变化代替某个接口结果

通常我们是通过一个方法返回拿到结果,如果要拿到几个功能执行后的返回结果,这种方式便行不通。再加上原子功能要保持闭环,独立,不能相互依赖值传递,就更不能走传统方式。

将Model改造一下,使用监听者模式,页面需要监听哪些数据,就在初始化时注册,原子功能执行时,根据参数修改对应数据,页面实时响应数据变化即可。

Service设计

Service分为两部分:语法解析、原子功能模块注册+执行。考虑到后期模块积累过多,造成加载耗时,要支持分桶加载模块能力,例如:现在有A、B、C,3个模块,但是页面只需要调用A、C模块,那么在初始化时,可以指定自定义模块桶为:AC。

可行性分析

目前,2020年优酷相关业务建设中,有相关雏形,可以完成上述部分设计,但必须申明一点,框架还有不完善之处,后续会朝着这个方向继续探索。后续完善后,会及时更新本文档,以及对应技术文档。

好处是明显的,框架升级后,琐碎繁复的代码,再也不需要重新copy+修改,原子功能完成只需一次,但可多次使用,并且随着加入开发的同学增多,后续业务使用时,会越来越方便。

写在最后

MVVM优化后框架并不是最终解,就像文章开头讲的,本文只讨论开发模式的可能演化、优化方向;希望大家在评论区留言指正、交流。

相关文章
|
7月前
|
JavaScript 前端开发 Java
MVP开发模式
MVP开发模式
108 3
|
7月前
|
运维 前端开发 JavaScript
现代化前端开发工具与框架的演进
随着Web应用的复杂性不断增加,前端开发工具和框架在不断演进,以应对日益复杂的需求。本文将从前端开发工具、主流框架以及未来发展趋势等方面进行探讨,帮助读者了解现代化前端开发技术的最新动态。
|
7月前
|
前端开发 JavaScript Java
现代化软件开发中的前后端分离模式
随着互联网技术的快速发展,现代化软件开发中的前后端分离模式逐渐成为主流。本文将介绍前后端分离的概念和优势,以及如何在不同的技术栈中实现这种模式。通过前后端分离,可以提高开发效率、降低耦合性,并且更好地满足用户需求。
124 0
|
7月前
|
前端开发
什么是MVVM架构?
MVVM是Model-View-ViewModel的简写。它本质上就是MVC的改进版。MVVM模式有助于将应用程序的业务和表示逻辑与用户界面 (UI) 清晰分离。 保持应用程序逻辑和UI之间的清晰分离有助于解决许多开发问题,并使应用程序更易于测试、维护和演变。 它还可以显著提高代码重用机会,并允许开发人员和UI设计人员在开发应用各自的部分时更轻松地进行协作。
119 2
|
7月前
|
前端开发 JavaScript 安全
前端模块化发展
前端模块化发展
|
7月前
|
前端开发 JavaScript 微服务
拥抱微前端:构建灵活可扩展的现代化应用
在当今快节奏的软件开发领域,传统的单体应用已经无法满足不断变化的需求。微前端架构作为一种创新的解决方案,通过将前端应用拆分成多个独立的小块,实现了更好的可维护性、可扩展性和独立部署能力。本文将介绍微前端的概念、原理以及实践方法,帮助读者深入了解并应用这一先进的技术。
|
前端开发 JavaScript 数据可视化
深入理解MVVM架构模式
深入理解MVVM架构模式
1091 0
|
存储 安全 Serverless
传统开发模式 vs 云开发模式
传统开发模式 vs 云开发模式
939 0
|
ARouter Java Maven
企业级项目组件化重构之路
前面几篇文章我们讲解了一个云音乐app的基础库搭建,今天我们就来对这个app进行**组件化代码重构**
|
开发框架 JavaScript 前端开发
前端工程化中重要概念之组件化开发框架
前端工程化中的组件化开发框架是一种重要的开发方式,它可以大幅提高前端开发效率和代码质量。本篇文章将介绍组件化开发框架的基本概念、原理和应用。
261 0