# 02. QFramework 的 MVC
QFramework 基于 MVC 的开发模式

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

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


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

![image.png](https://file.liangxiegame.com/902ef543-64b1-45ad-ae45-ea180ebec133.png)

场景结构如下所示：

![image.png](https://file.liangxiegame.com/1db2c50c-cb8b-4b1c-92f6-432ff4083105.png)

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

```csharp
using UnityEngine;
using UnityEngine.UI;

namespace QFramework.Example
{
    // Controller
    public class CounterAppController : MonoBehaviour
    {
        // View
        private Button mBtnAdd;
        private Button mBtnSub;
        private Text mCountText;
        
        // Model
        private 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 结果如下：


![282fcc3c-96fa-46e1-b4c6-7f4528b04271.gif](https://file.liangxiegame.com/1b934e4f-8f72-44c2-800a-a97f1e707950.gif)

非常简单。

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

首先是 Model、View、Controller

Model 的代码如下:
```csharp
// Model
private int mCount = 0;
```

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

View 的代码如下:
```csharp
// View
private Button mBtnAdd;
private Button mBtnSub;
private Text mCountText;
```

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

Controller 的代码，如下：

```csharp
void Start()
{
    ...
      
    // 监听输入
    mBtnAdd.onClick.AddListener(() =>
    {
        // 交互逻辑
        mCount++;
        // 表现逻辑
        UpdateView();        
    });
            
    mBtnSub.onClick.AddListener(() =>
    {
        // 交互逻辑
        mCount--;
        // 表现逻辑
        UpdateView();
    });
            
    UpdateView();
}
        
void UpdateView()
{
    mCountText.text = mCount.ToString();
}
```

以上就是 Controller 的代码。

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


```csharp
using UnityEngine;
using UnityEngine.UI;

namespace QFramework.Example
{
    // Controller
    public class CounterAppController : MonoBehaviour
    {
        // View
        private Button mBtnAdd;
        private Button mBtnSub;
        private Text mCountText;
        
        // Model
        private 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 地址：
* Gitee: https://gitee.com/liangxiegame/QFramework/blob/master/QFramework.cs
* Github: https://github.com/liangxiegame/QFramework/blob/master/QFramework.cs

导入之后，我们将 CounterAppController 的代码改成如下：

```csharp
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 结果如下：

![282fcc3c-96fa-46e1-b4c6-7f4528b04271.gif](https://file.liangxiegame.com/aa28ef15-11e9-49f2-9536-9db18b025a8f.gif)

运行正确。

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

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

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

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

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

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

## 更多内容

*   转载请注明地址：[liangxiegame.com](https://liangxiegame.com) （首发） 微信公众号：凉鞋的笔记
*   QFramework 主页：[qframework.cn](https://qframework.cn)
*   QFramework 交流群: 623597263
*   QFramework Github 地址: [https://github.com/liangxiegame/qframework](https://github.com/liangxiegame/qframework)
*   QFramework Gitee 地址：[https://gitee.com/liangxiegame/QFramework](https://gitee.com/liangxiegame/QFramework)
*   GamePix 独立游戏学院 & Unity 进阶小班地址：[https://www.gamepixedu.com/](https://www.gamepixedu.com/)



