02. QFramework 的 MVC

/

QFramework 基于 MVC 的开发模式

所以我们先从最熟知的 MVC 架构开始着手 QFramework 的学习。

我们先做一个非常简单的计数器应用。

首先我们使用 UGUI 创建一个最简单的界面,如下图所示:

场景结构如下所示:

复制完之后,我们创建一个脚本叫做 CounterAppController,代码如下:

using UnityEngine;
using UnityEngine.UI;

namespace QFramework.Example
{
    // Controllerpublic class CounterAppController : MonoBehaviour
    {
        // View
        private Button mBtnAdd;
        private Button mBtnSub;
        private Text mCountText;

        // Modelprivate int mCount = 0;

        void Start()
        {
            // View 组件获取
            mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();
            mBtnSub = transform.Find("BtnSub").GetComponent<Button>();
            mCountText = transform.Find("CountText").GetComponent<Text>();


            // 监听输入
            mBtnAdd.onClick.AddListener(() =>
            {
                // 交互逻辑
                mCount++;
                // 表现逻辑
                UpdateView();        
            });

            mBtnSub.onClick.AddListener(() =>
            {
                // 交互逻辑
                mCount--;
                // 表现逻辑
                UpdateView();
            });

            UpdateView();
        }

        void UpdateView()
        {
            mCountText.text = mCount.ToString();
        }
    }
}

代码很简单,这是一个非常简易的 MVC 的实现。

我们将此脚本挂在 Canvas 节点上,运行 Unity 结果如下:

非常简单。

此时我们还没有导入我们的 QFramework,不着急,我们先看看代码中所介绍的概念。

首先是 Model、View、Controller

Model 的代码如下:

// Model
private int mCount = 0;

非常简单,只有一个成员变量,但是在这里它其实并不算是一个 Model,他只是要在 View 中显示的一个数据而已,具体为什么不是 Model 我们在后边再说。

View 的代码如下:

// View
private Button mBtnAdd;
private Button mBtnSub;
private Text mCountText;

View 的代码也很简单,View 在 QFramework 的 MVC 定义里就是提供关键组件的引用,比如这三个组件是要在 Controller 代码里要用到的。而其他的例如 Canvas Scaler 等这些组件目前 Controller 不需要,所以就不用声明。

Controller 的代码,如下:

void Start()
{
    ...

    // 监听输入
    mBtnAdd.onClick.AddListener(() =>
    {
        // 交互逻辑
        mCount++;
        // 表现逻辑
        UpdateView();        
    });

    mBtnSub.onClick.AddListener(() =>
    {
        // 交互逻辑
        mCount--;
        // 表现逻辑
        UpdateView();
    });

    UpdateView();
}

void UpdateView()
{
    mCountText.text = mCount.ToString();
}

以上就是 Controller 的代码。

好了,我们回头再看下完整代码。

using UnityEngine;
using UnityEngine.UI;

namespace QFramework.Example
{
    // Controllerpublic class CounterAppController : MonoBehaviour
    {
        // Viewprivate Button mBtnAdd;
        private Button mBtnSub;
        private Text mCountText;

        // Modelprivate int mCount = 0;

        void Start()
        {
            // View 组件获取
            mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();
            mBtnSub = transform.Find("BtnSub").GetComponent<Button>();
            mCountText = transform.Find("CountText").GetComponent<Text>();


            // 监听输入
            mBtnAdd.onClick.AddListener(() =>
            {
                // 交互逻辑
                mCount++;
                // 表现逻辑
                UpdateView();        
            });

            mBtnSub.onClick.AddListener(() =>
            {
                // 交互逻辑
                mCount--;
                // 表现逻辑
                UpdateView();
            });

            UpdateView();
        }

        void UpdateView()
        {
            mCountText.text = mCount.ToString();
        }
    }
}

目前像计数器这样的逻辑,以上的代码完全没有问题。

但是我们要用发展的眼光看待问题。

假如这是一个初创项目,那么接下来很有可能需要添加大量的业务逻辑。

其中很有可能让 mCount 在多个 Controller 中使用,甚至需要针对 mCount 这个数据写一些其他逻辑,比如增加 mCount 则增加 5 个分数,或者 mCount 需要存储等,总之 mCount 在未来可能会发展成一个需要共享的数据,而 mCount 目前只属于 CounterAppController,显然在未来这是不够用的。

我们就需要让 mCount 成员变量变成一个共享的数据,最快的做法是吧 mCount 变量变成静态变量或者单例,但是这样虽然写起来很快,但是在后期维护额度时候会产生很多的问题。

而 QFramework 架构提供了 Model 的概念。

我们来使用一下。

我们先导入 QFramework 架构。

导入 QFramework 的方式非常简单,只需要复制 QFramework.cs 的代码到 Unity 工程中即可。

QFramework.cs 地址:

导入之后,我们将 CounterAppController 的代码改成如下:

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;
        }
    }
}

好了,代码引入了两个新的概念,一个是 Architecture,另一个是 Model。

Architecture 用于管理模块,或者说 Architecture 提供一整套架构的解决方案,而模块管理和提供 MVC 只是其功能的一小部分。

我们运行一下 Unity 结果如下:

运行正确。

好了,我们上手了 QFramework 提供的 MVC 架构。

这里要注意一点,Model 的引入是为了解决数据共享的问题,而不是说单只是为了让数据和表现分离,这一点是非常重要的一点。

数据共享分两种:空间上的共享和时间上的共享。

空间的共享很简单,就是多个点的代码需要访问 Model 里的数据。

时间上的共享就是存储功能,将上一次关闭 App 之前的数据存储到一个文件里,这次打开时获得上次关闭 App 之前的数据。

虽然我们上手了 MVC,但是这样的 MVC 还有很多问题,我们下一篇继续解决。

总结笔记

  1. 手动完成最简的 MVC 设计, 实现一个计数器

  2. QFramework 最简引入

  3. QFramework Architecture and Model 使用

    1. Model ➝ AbstractModel 抽象类

    2. Architecture ➝ Architecture 抽象类

      1. Architecture 注册 Model

    3. Controller ➝ IController

      1. Controller 需要获取 Architecture, 在 Architecture 抽象类 有一个静态方法后去 instance

      2. Controller 需要定义 Model, 所有数据操作都基于 Model

  4. Model 独立的功能, 数据共享和数据和表现分离