【Unity】MVC框架

一、介绍MVC   

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

    MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如一批统计数据可以分别用柱状图饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
  通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
  通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
  通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

二、MVC框架(偏向游戏编程)

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public abstract class Model
{
    //名字标识
    public abstract string Name { get; }

    //发送事件
    protected void SendEvent(string eventName,object data=null)
    {
        MVC.SendEvent(eventName, data);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class View : MonoBehaviour {

    //名字标识
    public abstract string Name { get; }
    
    //事件关心列表
    [HideInInspector]
    public List<string> AttentionList = new List<string>();

    //初始化事件关心列表,在注册视图的时候被调用
    public virtual void RegisterAttentionEvent() { }

    //处理事件
    public abstract void HandleEvent(string name, object data);

    //发送事件
    protected void SendEvent(string eventName, object data = null)
    {
        MVC.SendEvent(eventName, data);
    }

    //获取模型
    protected T GetModel<T>()
        where T:Model
    {
        return MVC.GetModel<T>() as T;
    }


}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public abstract class Controller
{
    //执行事件
    public abstract void Execute(object data);


    //获取模型
    protected T GetModel<T>()
        where T : Model
    {
        return MVC.GetModel<T>() as T;
    }


    //获取视图
    protected T GetView<T>()
        where T : View
    {
        return MVC.GetView<T>() as T;
    }
    //注册模型
    protected void RegisterModel(Model model)
    {
        MVC.RegisterModel(model);
    }
    //注册视图
    protected void RegisterView(View view)
    {
        MVC.RegisterView(view);       
    }
    //注册控制器
    protected void RegisterController(string eventName,Type controllerType)
    {
        MVC.RegisterController(eventName, controllerType);
    }

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public static class MVC
{
    //资源
    //名字 -- model(保存的是实例对象)
    public static Dictionary<string, Model> Models = new Dictionary<string, Model>();
    //名字 -- view(保存的是实例对象)
    public static Dictionary<string, View> Views = new Dictionary<string, View>();
    //事件名字 -- 类型 (保存的是类)
    public static Dictionary<string, Type> ComandMap = new Dictionary<string, Type>();
    
    //注册view,注意这里的view其实是由 继承于View的子类上溯而来(下同)
    public static void RegisterView(View view)
    {
        //防止view的重复注册
        if(Views.ContainsKey(view.Name))
        {
            Views.Remove(view.Name);
        }
        view.RegisterAttentionEvent();//调用视图方法,注册视图关心事件,存放在关心事件列表中
        Views[view.Name] = view;
    }
    //注册model
    public static void RegisterModel(Model model)
    {
        Models[model.Name] = model;
    }
    //注册controller 将一个事件执行器放入字典,以eventName为键,Type类型是类,就是放入一个需要被实例化的类,使用的时候必须要实例化再使用
    public static void RegisterController(string eventName,Type controllerType)
    {
        ComandMap[eventName] = controllerType;
    }
   
    //获取model,T是外部传进来的模型脚本,该脚本必须继承自Model
    public static T GetModel<T>()
        where T:Model
    {
        foreach(var m in Models.Values)
        {
            //m肯定是Model类型,但是这个m 是由 继承于Model的脚本上溯而来的,这里会进行下溯 看看m是否等于T
            if (m is T)
            {
                //若等于,则先强转为T脚本 再返回.
                return (T)m;
            }
        }
        return null;
    }

    //获取view
    public static T GetView<T>()
        where T : View
    {
        foreach (var v in Views.Values)
        {
            if (v is T)
            {
                return (T)v;
            }
        }
        return null;
    }

    //发送事件(对于外部调用者来说该方法是发送事件,对于内部方法来说是不同的控制器和视图处理事件),命名上不要求和我一致,只要见名知意即可.
    public static void SendEvent(string eventName,object data=null)
    {
        //在这里可发现一个事件对应一个Controller处理,具体事件继承于抽象事件,一个具体事件的诞生首先要进行继承于Controller 重写Execute 注册入CommandMap字典三步骤
        //controller 执行,eventName是事件名称,若在控制器字典内存在该事件名称,则肯定会有一个控制器去处理该事件
        if(ComandMap.ContainsKey(eventName))
        {
            //t脚本类是继承于Controller类的,不然下面无法转换为Controller
            Type t = ComandMap[eventName];
            //根据字典取出来的t,去实例化一个对象,并且object转化为Controller类型,因为t对象继承于Controller所以可以转化
            Controller c = Activator.CreateInstance(t) as Controller;           
            //执行被t所重写的Execute方法,data是传入的数据(object类型)
            c.Execute(data);
        }
        //view处理
        //遍历所有视图,注意:一个视图允许有多个事件,而且一个事件可能会在不同的视图触发,而事件的内容不确定(事件可理解为触发消息)
        foreach(var v in Views.Values)
        {
            //视图v的关心事件列表中存在该事件
            if(v.AttentionList.Contains(eventName))
            {
                //让视图v执行该事件eventName,附带参数data
                //HandleEvent方法是通过switch case的形式处理不同的事件
                v.HandleEvent(eventName, data);
            }
        }
    }
}

思考:为什么Controller类的注册方法与View和Model有所不同?

    其实,这种注册方法只是图方便,因为如果传递的是一个对象进来,需要new XxxController(),而Controller直接传递一个Type就无须new,直接用typeof(XxxxController)转化为Type,而注册的时候肯定是知道这个Controller处理什么事件的.

这里有个内在关系,即Controller只处理Controller名字的事件,这个要用一个静态类来保存这些Controller的名字,我们是通过这些名字来注册和发送事件的,这些名字也是View视图关心事件的名字。

假设:我新建了一个控制器StartGameController,它的名字叫E_StartGame ,E代表的是事件Event的意思

而且 我新建了一个视图MainView,它的名字叫V_Main,V代表的是视图View的意思,该视图的关心事件注册了E_StartGame

当我们调用MVC.SendEvent("E_StartGame")时,就会触发控制器StartGameController的Execute方法(一对一处理),以及视图MainView的HandleEvent方法(一对多处理),HandleEvent方法通过Switch(eventName) case "E_StartGame": ... break; 这样来处理E_StartGame事件.

上面的"E_StartGame"很容易写错,所以用一个静态类来保存,防止出错。

注册视图和注册模型的名字,同样也可以分别用两个静态类来保存。


猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/80757261
今日推荐