我们回顾一下目前的代码,如下;
using UnityEngine;
using UnityEngine.UI;
namespace QFramework.Example
{
// 1. 定义一个 Model 对象
public class CounterAppModel : AbstractModel
{
public int Count;
protected override void OnInit()
{
Count = 0;
}
}
// 2.定义一个架构(提供 MVC、分层、模块管理等)
public class CounterApp : Architecture<CounterApp>
{
protected override void Init()
{
// 注册 Model
this.RegisterModel(new CounterAppModel());
}
}
// Controller
public class CounterAppController : MonoBehaviour , IController /* 3.实现 IController 接口 */
{
// View
private Button mBtnAdd;
private Button mBtnSub;
private Text mCountText;
// 4. Model
private CounterAppModel mModel;
void Start()
{
// 5. 获取模型
mModel = this.GetModel<CounterAppModel>();
// View 组件获取
mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();
mBtnSub = transform.Find("BtnSub").GetComponent<Button>();
mCountText = transform.Find("CountText").GetComponent<Text>();
// 监听输入
mBtnAdd.onClick.AddListener(() =>
{
// 6. 交互逻辑
mModel.Count++;
// 表现逻辑
UpdateView();
});
mBtnSub.onClick.AddListener(() =>
{
// 7. 交互逻辑
mModel.Count--;
// 表现逻辑
UpdateView();
});
UpdateView();
}
void UpdateView()
{
mCountText.text = mModel.Count.ToString();
}
// 3.
public IArchitecture GetArchitecture()
{
return CounterApp.Interface;
}
private void OnDestroy()
{
// 8. 将 Model 设置为空
mModel = null;
}
}
}
现在,数据共享的问题通过 引入 Model 解决了。
这里再次强调一下,需要共享的数据放 Model 里,不需要共享的,能不放就不放。
虽然引入了 Model,但是这套代码随着项目规模的发展还是有很多的问题。
其中最严重也最常见的就是 Controller 会越来越臃肿。
我们简单分析一下为什么 Controller 会越来越臃肿,我们先看下监听用户输入部分的代码,如下:
// 监听输入
mBtnAdd.onClick.AddListener(() =>
{
// 交互逻辑
mModel.Count++;
// 表现逻辑
UpdateView();
});
mBtnSub.onClick.AddListener(() =>
{
// 交互逻辑
mModel.Count--;
// 表现逻辑
UpdateView();
});
在处理用户输入的代码中,笔者写了注释,交互逻辑 和 表现逻辑。
什么是交互逻辑 和 表现逻辑?
非常简单。
交互逻辑,就是从用户输入开始到数据变更的逻辑
顺序是 View->Controller->Model
表现逻辑,就是数据变更到在界面显示的逻辑
顺序是 Model->Controller->View
如下图所示:
虽然交互逻辑和表现逻辑理解起来简单,但是它们非常重要,因为 QFramework 接下来的概念都是围绕这两个概念展开的。
View、Model 以及 Controller 的交互逻辑和表现逻辑形成了一个闭环。构成了完整的 MVC 闭环。
而 Controller 本身之所以臃肿,是因为,它负责了两种职责,即改变 Model 数据 的交互逻辑,以及 Model 数据变更之后更新到界面的表现逻辑。
而在一个有一定规模的项目中,表现逻辑和交互逻辑非常多。而一个 Controller 很容易就做到上千行代码。
而大部分的 MVC 方案,解决 Controller 臃肿用的是引入 Command 的方式,即引入命令模式,通过命令来分担 Controller 的交互逻辑的职责。
QFramework 也是使用了同样的方式解决 Controller 臃肿的问题。
我们将代码改成如下:
using UnityEngine;
using UnityEngine.UI;
namespace QFramework.Example
{
// 1. 定义一个 Model 对象
public class CounterAppModel : AbstractModel
{
public int Count;
protected override void OnInit()
{
Count = 0;
}
}
// 2.定义一个架构(提供 MVC、分层、模块管理等)
public class CounterApp : Architecture<CounterApp>
{
protected override void Init()
{
// 注册 Model
this.RegisterModel(new CounterAppModel());
}
}
// 引入 Command
public class IncreaseCountCommand : AbstractCommand // ++
{
protected override void OnExecute()
{
this.GetModel<CounterAppModel>().Count++;
}
}
public class DecreaseCountCommand : AbstractCommand // ++
{
protected override void OnExecute()
{
this.GetModel<CounterAppModel>().Count--;
}
}
// Controller
public class CounterAppController : MonoBehaviour , IController /* 3.实现 IController 接口 */
{
// View
private Button mBtnAdd;
private Button mBtnSub;
private Text mCountText;
// 4. Model
private CounterAppModel mModel;
void Start()
{
// 5. 获取模型
mModel = this.GetModel<CounterAppModel>();
// View 组件获取
mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();
mBtnSub = transform.Find("BtnSub").GetComponent<Button>();
mCountText = transform.Find("CountText").GetComponent<Text>();
// 监听输入
mBtnAdd.onClick.AddListener(() =>
{
// 交互逻辑
this.SendCommand<IncreaseCountCommand>();
// 表现逻辑
UpdateView();
});
mBtnSub.onClick.AddListener(() =>
{
// 交互逻辑
this.SendCommand<DecreaseCountCommand>();
// 表现逻辑
UpdateView();
});
UpdateView();
}
void UpdateView()
{
mCountText.text = mModel.Count.ToString();
}
// 3.
public IArchitecture GetArchitecture()
{
return CounterApp.Interface;
}
private void OnDestroy()
{
// 8. 将 Model 设置为空
mModel = null;
}
}
}
代码很简单,我们用流程图表示如下:
运行 Unity,结果如下:
没有变化,运行正确。
大家可能会问,一个简单的数据加减操作,至于创建一个 Command 对象来承担么?看不出来好处呀,反而代码更多了。
如果整个项目只有一个简单的数据加减操作,那使用 Command 有点多此一举,但是一般的项目的交互逻辑,是非常复杂的,代码量也非常多,整个时候使用 Command 词汇发挥作用。
具体发挥什么作用,使用 Command 可以带来很多便利,比如:
- Command 可以复用,Command 也可以调用 Command
- Command 可以比较方便实现撤销功能,如果 App 或者 游戏需要的话
- 如果遵循一定规范,可以实现使用 Command 跑自动化测试。
- Command 可以定制 Command 队列,也可以让 Command 按照特定的方式执行
- 一个 Command 也可以封装成一个 Http 或者 TCP 里的一次数据请求
- Command 可以实现 Command 中间件模式
- 等等
OK,通过引入 Command,帮助分担了 Controller 的交互逻辑。使得 Controller 成为一个薄薄的一层,在需要修改 Model 的时候,Controller 只要调用一句简单的 Command 即可。
Command 最明显的好处就是:
- 就算代码再乱,也只是在一个 Command 对象里乱,而不会影响其他的对象。
- 讲方法封装成命令对象,可以实现对命令对象的组织、排序、延时等操作。
更多好处会随着大家的实践慢慢体会到。
当前的 MVC 流程如下:
这篇内容就这些。
更多内容
- 转载请注明地址:liangxiegame.com (首发) 微信公众号:凉鞋的笔记
- QFramework 主页:qframework.cn
- QFramework 交流群: 623597263
- QFramework Github 地址: https://github.com/liangxiegame/qframework
- QFramework Gitee 地址:https://gitee.com/liangxiegame/QFramework
- GamePix 独立游戏学院 & Unity 进阶小班地址:https://www.gamepixedu.com/