在前面我们已经通过基础的学习了解了QFramework的架构以及引入Command和引入Event。那么
如果我们想支持CounterApp的存储功能,应该如何做呢?
如果时候未来我们需要存储的数据非常多的时候,Model 层就会充斥大量存储、加载相关的代码。
还有就是,我们以后如果不想使用 PlayperPrefs 了,想使用 EasySave 或者 SQLite 的时候,就会
造成大量的修改工作量。
于是 QFramework 提供了一个 Utility 层,专门用来解决上述两个问题的,使用方法非常简单:
using UnityEngine;
using UnityEngine.UI;
// 1. 定义一个 Model 对象
public class CounterAppModel : AbstractModel
{
private int mCount;
public int Count
{
get => mCount;
set
{
if (mCount != value)
{
mCount = value;
PlayerPrefs.SetInt(nameof(Count),mCount);
}
}
}
protected override void OnInit()
{
var storage = this.GetUtility();
Count = storage.LoadInt(nameof(Count));
// 可以通过 CounterApp.Interface 监听数据变更事件
CounterApp.Interface.RegisterEvent(e =>
{
this.GetUtility().SaveInt(nameof(Count), Count);
});
}
}
// 定义 utility 层
public class Storage : IUtility
{
public void SaveInt(string key, int value)
{
PlayerPrefs.SetInt(key,value);
}
public int LoadInt(string key, int defaultValue = 0)
{
return PlayerPrefs.GetInt(key, defaultValue);
}
}
// 2.定义一个架构(提供 MVC、分层、模块管理等)
public class CounterApp : Architecture
{
protected override void Init()
{
// 注册 Model
this.RegisterModel(new CounterAppModel());
// 注册存储工具的对象
this.RegisterUtility(new Storage());
}
}
// 定义数据变更事件
public struct CountChangeEvent // ++
{
}
// 引入 Command
public class IncreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
this.GetModel().Count++;
this.SendEvent(); // ++
}
}
public class DecreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
this.GetModel().Count--;
this.SendEvent(); // ++
}
}
// 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();
// View 组件获取
mBtnAdd = transform.Find("BtnAdd").GetComponent
这样当我们,想要将 PlayerPrefs 方案替换成 EasySave 的时候,只需要对 Storage 里的代码进行修改即可。
最后给出流程图,如下:
成就问题:
这里我们假设一个功能,即策划提出了一个成就达成的功能,即 Count 到 10 的时候,触发一个点击达人成就,点击二十次 则触发一个 点击专家成就。
但是这个时候策划说,希望再增加一个当点击 - 号到 -10 时,触发一个 点击菜鸟成就,然后策划还说,点击达人 和 点击专家 成就太容易达成了,需要分别改成 1000 次 和 2000 次。
而这次策划提出的需求,需要我们修改两处的代码,即 IncreaseCountCommand 里需要修改数值为 1000 和 2000,然后再 DecreaseCountCommand 增加一个判断逻辑。
一次提出的需求,结果造成了多处修改,这说明代码有问题。
首先像这种规则类的逻辑,比如分数统计或者成就统计等代码,不适合分散写在 Command 里,而适合统一写在一个对象里,而这种对象,在 QFramework 里有提供,就是 System 对象。
代码如下:
using UnityEngine;
using UnityEngine.UI;
// 1. 定义一个 Model 对象
public class CounterAppModel : AbstractModel
{
private int mCount;
public int Count
{
get => mCount;
set
{
if (mCount != value)
{
mCount = value;
PlayerPrefs.SetInt(nameof(Count),mCount);
}
}
}
protected override void OnInit()
{
var storage = this.GetUtility();
Count = storage.LoadInt(nameof(Count));
// 可以通过 CounterApp.Interface 监听数据变更事件
CounterApp.Interface.RegisterEvent(e =>
{
this.GetUtility().SaveInt(nameof(Count), Count);
});
}
}
public class AchievementSystem : AbstractSystem // +
{
protected override void OnInit()
{
var model = this.GetModel();
this.RegisterEvent(e =>
{
if (model.Count == 10)
{
Debug.Log("触发 点击达人 成就");
}
else if (model.Count == 20)
{
Debug.Log("触发 点击专家 成就");
} else if (model.Count == -10)
{
Debug.Log("触发 点击菜鸟 成就");
}
});
}
}
// 定义 utility 层
public class Storage : IUtility
{
public void SaveInt(string key, int value)
{
PlayerPrefs.SetInt(key,value);
}
public int LoadInt(string key, int defaultValue = 0)
{
return PlayerPrefs.GetInt(key, defaultValue);
}
}
// 2.定义一个架构(提供 MVC、分层、模块管理等)
public class CounterApp : Architecture
{
protected override void Init()
{
// 注册 System
this.RegisterSystem(new AchievementSystem()); // +
// 注册 Model
this.RegisterModel(new CounterAppModel());
// 注册存储工具的对象
this.RegisterUtility(new Storage());
}
}
// 定义数据变更事件
public struct CountChangeEvent // ++
{
}
// 引入 Command
public class IncreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
var model = this.GetModel();
model.Count++;
this.SendEvent(); // ++
}
}
public class DecreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
this.GetModel().Count--;
this.SendEvent(); // ++
}
}
// 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();
// View 组件获取
mBtnAdd = transform.Find("BtnAdd").GetComponent();
mBtnSub = transform.Find("BtnSub").GetComponent();
mCountText = transform.Find("CountText").GetComponent();
// 监听输入
mBtnAdd.onClick.AddListener(() =>
{
// 交互逻辑
this.SendCommand();
});
mBtnSub.onClick.AddListener(() =>
{
// 交互逻辑
this.SendCommand(new DecreaseCountCommand(/* 这里可以传参(如果有) */));
});
UpdateView();
// 表现逻辑
this.RegisterEvent(e =>
{
UpdateView();
}).UnRegisterWhenGameObjectDestroyed(gameObject);
}
void UpdateView()
{
mCountText.text = mModel.Count.ToString();
}
// 3.
public IArchitecture GetArchitecture()
{
return CounterApp.Interface;
}
private void OnDestroy()
{
// 8. 将 Model 设置为空
mModel = null;
}
}
到此,我们就接触到了 QFramework 架构所提供的核心概念。
QFramework 总共分四个层级,即
除了四个层级,还接触了为 Controller 的交互逻辑减负的 Command 和 为表现逻辑减负的 Event。
还有一个非常重要的 CQRS 原则的简易版本,Command->Model->State Changed Event。
到目前为止 QFramework 的基本用法我们过了一遍了。