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

相关推荐
好好研究14 小时前
总结SSM设置欢迎页的方式
xml·java·后端·mvc
Elieal21 小时前
Spring MVC 全局异常处理实战
spring·mvc·状态模式
树码小子2 天前
SpringMVC(3):传递 Json
spring·json·mvc
树码小子2 天前
SpringMVC(4):获取参数,上传文件
spring·mvc
秃头续命码农人3 天前
谈谈对Spring、Spring MVC、SpringBoot、SpringCloud,Mybatis框架的理解
java·spring boot·spring·mvc·maven·mybatis
树码小子3 天前
SpringMVC(2)传入请求参数
spring·mvc
树码小子3 天前
SpringMVC(1)初识MVC
spring·mvc
风景的人生4 天前
请求参数相关注解
spring·mvc
空空kkk5 天前
Spring、Spring MVC、SpringBoot的欢迎页配置
spring boot·spring·mvc
yangminlei5 天前
Spring MVC 响应机制综合实践:页面、数据、JSON 与响应配置
spring·json·mvc