UI框架与MVC模式详解(3)——MVC\MVP\MVVM

【PDI模式】

前文中,我们详细讲解了为实现一个涉及UI的功能所必须得三者,简称PDI:

  • Panel类:主要实现交互逻辑、显示逻辑的地方以及保存界面相关的数据的地方
  • Data类:数据管理类,主要是业务相关的数据
  • Inter类:Panel类和Data类之间数据联通的中间者

根据功能的复杂程度来决定这三者是拆开还是合在一块。功能简单的情况下,直接一个Panel类搞定,复杂时要拆开成三个。

如果要实现ABCDE五个复杂的功能,那我们是不是一共需要15个类?

这要看五个功能的业务相关性,业务相关性越高所需的Data类和Inter类越少,也就是我们前文所说的多个Panel类的数据可能来自同一个Data类。

根据这个可以看到游戏开发和互联网开发的一个重要的不同地方:

游戏开发中绝大部分业务数据都在客户端中,数据几乎以标准的树状图呈现,一般一个或多个Panel类的数据来自一个Data类

互联网开发中绝大部分业务数据都在服务端中,数据分布在不同根的树中,一般一个或多个Panel类中的数据来自多个Data类

后者在界面的开发上会复杂一些,因为多了些数据异步获取的情况,但遵循前文所说的方式,都能顺利解决。

【MVC】

MVC指的是模型Model、视图View、控制器Controller,他们的关系图如下:

View从Model中Get数据,Model数据变化时通知View,用户与界面的操作产生Action被发到Controller,Controller处理后将操作更新到Model和View,两者再做各自的处理。

那么能说PDI和MVC的对应关系为Model是Data、View是Panel、Controller是Inter吗?

这两者不是一回事。

MVC是整个UI框架层级上的模式,PDI是在已有的UI框架模式下(不一定是MVC)的具体功能上的模式,他们的核心都是实现逻辑和数据的分离。

我们将从以下几个方面区别:

交互

以屏幕为输入设备的基础交互只有点击和移动,结合UI组件,可以拓展出双击、长按、拖拽、多指点击等更为丰富的交互操作。

输入设备还有鼠标、键盘、手柄等,每种输入设备都有基础的交互事件

一般来说,输入设备会将基础交互事件传递给操作系统,操作系统将其传递给应用程序,应用程序自己处理基础交互。

视图

我们可以将常用的一系列逻辑分离出来做组件化形成基础的UI组件(即View组件),这些组件是:文本Text、图像Image、按钮Button

由这些基础组件可以拓展形成常用组件,包括:

|--------|------------|---------------------|-------------------------|------------------|
| 组件 | Unity | Android | iOS | Web (Element UI) |
| 可输入的文本 | InputField | EditText | UITextField | <el-input> |
| 可切换的按钮 | Toggle | CheckBox | UISwitch | <el-checkbox> |
| 滑动条 | Slider | ProgressBar SeekBar | UISlider UIProgressView | <el-progress> |
| 滑动列表 | SrollView | ListView | UIScrollView | <el-list> |
| 下拉列表 | DropDown | Spinner | UIPickerView | <el-select> |

当然,还有一些常用组件没列举出来,不同项目组也会根据项目需要做自定义的组件。后来,还有用于调整布局的ViewGroup组件

这些常用组件会构成的组件库,有些开源的组件库,例如:

35+前端UI组件库一览(按star数排名) - 掘金 (juejin.cn)

Android最全UI库合集_android ui库-CSDN博客

在Unity中,UI组件库有UGUI、NGUI、FairUI,但绝大多数都用UGUI。

这些成熟的组件库一般会帮我们处理好用户与View的交互,我们通过接收或者监听得到交互结果。

假设我们只有最基础的组件,要实现前文中的一个显示项,按照逻辑和数据耦合的方式,我们可能在一个类中做完以下事情:

  1. 处理操作系统传递的基础交互事件
  2. View组件的逻辑,也即视图相关的逻辑,基本是和渲染相关的逻辑
  3. 前文中有View组件后的其他各种逻辑

如果有8个不同的显示项,那么这3个部分岂不是要写8遍。作为后来者的我们,肯定可以想到1和2是重复的,其中的逻辑可以提取出来

有个Manager会处理操作系统传递的基础交互事件,生成常用交互事件,决定交互事件与哪个View组件交互。这属于很底层的功能,虽然不属于操作系统的范畴,但也会提供,我们在项目中一般不会自己做。

随后View组件会将交互结果传递出去。

UI框架中的MVC

应用程序中的界面是很多的,我们无法预测界面是什么样的,以及有多少个界面

一个界面通常是由多个View组件组合成的,在业务逻辑中会去持有所有需要的View组件,其是整个框架中的View层。

一个界面的消失和另一个界面的出现是和用户交互有关的,操作系统提供的会保证交互从View组件,也即View层中传递出来。

我们需要管理控制界面之间的切换和界面的生命周期,这就是UI框架中,UIManager的最重要的职责,其相当于Controller。

我们针对每个界面的业务逻辑和数据就是Model。

View和Modle以及部分Controller的实现和具体业务是息息相关的,无法在框架层面上去实现,但在框架上可以去规定不同部分应该在哪实现。

而Controller的核心职责却可以提出来,可以自己实现,一般在Web开发和游戏开发中,要项目自己去实现。

而在Android的架构中,其Activity相当于一个界面,界面的切换和生命周期Android提供管理。iOS的UIView和UIViewController类似

【 MVC职责划分】

View层

  1. 负责获取界面上的View组件,PanelView,这部分代码比较固定,可以做成自动化生成
  2. 负责处理不同View之间的布局逻辑,PanelViewLayout,同样可以自动化生成

Controller层

  1. 负责接收不同组件上的交互事件和其他消息,PanelController,同样可以自动化生成
  2. 负责接收UIManager传来的Panel生命周期调用,PanelLifeCycle,同样可以自动化生成

Model层

  1. 负责业务和界面逻辑实现,PanelLogic,需自定义实现
  2. 负责管理业务和界面数据,PanelData,需自定义实现

对比而言,文中说的LDI更像是Model,也不仅仅用于UI开发,用于其他方开发可以叫LDI,其中L是logic

【MVC的优缺点】

优点

1.实现业务和视图的分离,提高了Model的复用性。

2.职责划分明确,便于拓展维护

缺点

1.错误将业务逻辑放在Controller中导致其越加臃肿,框架只是规定了代码要写在哪里,实际个人在哪都可以写,如果对MVC理解不清楚,很容易认为业务逻辑属于Controller、

2.View和Modle直接交互可能会导致引发连锁反应而难以查证。(在游戏开发中这种情况较少)

【MVP】

P是Presenter,View 和 Model 不相互持有,都通过 Presenter 做中转,如下图所示:

在MVP中,P相当于Controller,V需要的数据通过P从M中获取,为此有如下改动:

  1. 需要把原来V直接从M中获取数据的方法都转移到P中
  2. 原来M中的Logic部分中与View相关的逻辑也需要转移到P中

同时,为了规范每个地方的职责,需要用接口约束,具体的M、V、P类都要基础M、V、P接口

MVP的优点是通过接口做到了进一步解耦和规范,缺点是会引入更多的代码,增加接口复杂性。也就是使用接口的优缺点。

【MVVM】

VM是ViewModel,主要指视图的相关数据和逻辑,前文说过Panel自己的与界面相关的数据,包括交互数据和业务数据,如下图所示:

在MVC中,业务数据直接就是界面所需的交互数据,但在MVVM中,会有一个业务数据和界面数据的映射,例如业务数据中的UserName字段界面数据中可能仍是原样显示,但地区需要从"中国"显示为"China"

在MVC中,与界面有关的逻辑在M中,MVP中,与界面有关的逻辑在P中,但在MVVM中,这些逻辑会放在VM中。

在MVVM中,View中需要新增一个绑定逻辑,为PanelViewBind,其将V中的组件的数据与VM中的数据绑定,以便于双方中有变化时会自动通知对方取修改值。

更进一步的,需要绑定逻辑。

此时,View会持有VM,VM不会持有V,VM通过绑定逻辑将数据和逻辑同步到V,各种给View组件赋值的逻辑可以省略。

这是MVVM的一大特点,在MVC和MVP中,实现界面交互和显示逻辑时都需要持有View组件,如果View组件没有事先创建好,就不好先实现逻辑。

而在MVVM中,两者是可以分开的,并行进行。在MVC中,可能需要程序去完成界面,而在MVVM中界面可以由交互完成。

注意,PanelViewBind中只需要调用接口实现单向或者双向绑定即可,以便于自动化生成。

调用的接口需要框架去实现,即在框架层面上支持数据绑定。一般情况下,在视图中只显示而无需编辑的数据用单向绑定,需要编辑的数据才用双向绑定。

【MVC框架:GameFrameWork的UI模块】

这里只做简述,详细的请看他人解析源码

在GF中,一个Panel对应一个UIForm,定义了界面的9种状态,也即生命周期:

  • OnInit和OnRecycle 界面初始化和回收,其会界面打开和关闭会被调用到,(个人感觉在界面资源被加载和卸载时调用即可)
  • OnOpen和OnClose 界面打开和关闭,会在用户主动打开和关闭界面的流程种被调用到
  • OnCover和OnReveal 界面被遮挡和从遮挡种恢复,例如A界面先打开,B界面后打开,那么A界面被遮挡,A界面的OnCover会被调用到,随后B界面再关闭,A界面的OnReveal会被调用
  • OnUpdate 界面更新
  • OnPause和OnResume 界面暂停和从暂停种恢复,主要是界面被遮挡时会否还做Update

多个UIForm可以在一个UIGroup中,UIGroup中根据Depth去管理界面的在OnCover、OnReveal、OnPause、OnResume之间的状态变化,代码在Refresh方法中

UIManager会管理不同的UIGroup,通过OpenUIForm和ColseUIForm打开关闭界面,再从UIManger到UIGroup到UIForm做层层调用

这九种状态足以应对觉大多数情况,其他不同UI框架的定义和处理大差不差。

UGF中,有一个UIForm继承IUIForm接口,其持有UIFormLogic,在界面不同的状态时,即而调用到UIFormLogic中的OnInit至OnRecycle等九个方法。

UGuiForm继承自UIFormLogic,其中的方法和字段基本和UIFormLogic相同,我们自己的界面继承自UGuiForm即可。

这个UI框架,主要是实现了生命周期的管理,MVC的职责并没有划分明确,也没做自动化生成,感兴趣可以按照上文所说的职责划分拓展实现一下。

【MVVM框架:LoxodonFramework的UI模块】

核心在于看看其数据绑定的实现,这块比较复杂些,之后单独说说,可以先参考下源码

【参考】

正确认识 MVC/MVP/MVVM - 掘金 (juejin.cn)

https://zhuanlan.zhihu.com/p/99443196

相关推荐
李小白665 小时前
Spring MVC(上)
java·spring·mvc
王ASC11 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web
撒呼呼11 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
zybishe2 天前
免费送源码:Java+ssm++MVC+HTML+CSS+MySQL springboot 社区医院信息管理系统的设计与实现 计算机毕业设计原创定制
java·hadoop·sql·zookeeper·html·json·mvc
nie66688882 天前
springmvc的拦截器,全局异常处理和文件上传
spring·mvc
王ASC2 天前
Springboot访问到Controller中不存在的接口BUG
spring boot·后端·mvc
de之梦-御风3 天前
【进阶编程】MVVM的物理架构目录
架构·mvc·.net
m0_748247803 天前
WebMvcConfigurer和WebMvcConfigurationSupport(MVC配置)
mvc
喵小狸3 天前
Spring MVC 中,处理异常的 6种方式
python·spring·mvc
波多尔斯基3 天前
CompilerGenerated与GeneratedCode区别
c#·.net·mvvm·.net core