在当今的软件开发领域,MVC(Model-View-Controller)设计模式已经成为了一种广泛使用的架构模式。它为应用程序提供了一种结构化的方法,将数据、用户界面和业务逻辑分开,从而使得应用程序更易于维护、扩展和重用。
一、MVC的概述
MVC是一种分层的设计,主要用于解决UI交互方面的经验体系。它包括Model、View和Controller三部分,分别负责数据的存储、视图界面的显示和业务逻辑的处理。MVC模式有助于降低代码的耦合度,实现界面、数据和逻辑的分离,从而易于代码的维护和拓展。
二、MVC的组成部分
模型(Model):模型是应用程序的数据和业务逻辑的核心。它负责处理与数据相关的所有操作,例如数据的增删改查、验证和持久化等。模型是与数据源直接交互的部分,不涉及用户界面或用户输入的处理。
视图(View):视图是应用程序的用户界面。它负责显示数据给用户,并接收用户的输入。视图只关注如何展示数据,而不关心数据从哪里来或应该如何处理。
控制器(Controller):控制器是模型和视图之间的中介。它负责接收用户的输入,处理这些输入,并更新模型和视图。控制器的主要任务是处理用户交互,并根据用户的操作更新数据和界面。控制器不包含任何与数据或用户界面相关的代码,而是专注于处理用户交互和协调模型和视图之间的通信。
三、MVC的优势
MVC模式的优点主要表现在以下几个方面:
解耦:MVC模式将应用程序的不同部分(数据、用户界面和业务逻辑)分离,降低了它们之间的耦合度。这使得开发人员可以独立地专注于各自的部分,提高了开发效率和代码的可维护性。
代码组织:MVC模式为应用程序提供了一种清晰的代码组织结构,使得代码更加模块化和易于管理。这种结构也有助于团队之间的协作,使得不同的开发人员可以专注于不同的部分。
可扩展性:由于MVC模式将应用程序的不同部分分离,因此当应用程序需要扩展时,可以更容易地添加新的功能或模块,而不会对现有的代码造成太大的影响。
可重用性:MVC模式使得模型、视图和控制器的分离,这使得它们可以独立地重用。例如,同一个模型可以与不同的视图或控制器一起使用,或者同一个视图可以用在不同的应用程序中。
易于测试和维护:由于MVC模式将应用程序的不同部分分离,因此可以更容易地对每个部分进行单元测试和集成测试。此外,由于代码的模块化结构,使得代码更易于维护和修改。
四、Unity游戏开发中的MVC
这里提供一个MVC模式的基本功能框架。
Model
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Model基类
public class BaseModel
{
public BaseController controller;
//有参构造
public BaseModel(BaseController control)
{
this.controller = control;
}
//无参构造
public BaseModel()
{
}
//初始化
public virtual void Init()
{
}
}
View
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//面板基类,因为需要挂载到场景对象上需要继承MonoBehaviour
public class BaseView : MonoBehaviour
{
//面板ID属性
public int ViewId { get; set; }
//面板控制器
public BaseController Controller { get; set; }
//面板cavans
protected Canvas _canvas;
protected Dictionary<string,GameObject> _ObjCache = new Dictionary<string, GameObject>();//面板中对象缓存
private bool _isInit = false; //是否初始化
void Awake()
{
_canvas = gameObject.GetComponent<Canvas>();
OnAwake();
}
void Start()
{
OnStart();
}
protected virtual void OnAwake()
{
}
protected virtual void OnStart()
{
}
//调用指定控制器的注册方法
public void ApplyControllerFunc(int controllerKey, string eventName, params object[] args)
{
this.Controller.ApplyControllerFunc(controllerKey,eventName,args);
}
//调用自己控制器的注册方法
public void ApplyFunc(string eventName, params object[] args)
{
this.Controller.ApplyFunc(eventName,args);
}
//关闭界面
public virtual void Close(params object[] args)
{
SetVisible(false);
}
//销毁界面
public void DestroyView()
{
Controller = null;
Destroy(gameObject);
}
//初始化数据
public virtual void InitData()
{
_isInit = true;
}
//初始化UI
public virtual void InitUI()
{
}
public bool IsInit()
{
return _isInit;
}
public bool IsShow()
{
return _canvas.enabled == true;
}
public virtual void Open(params object[] args)
{
}
public void SetVisible(bool value)
{
this._canvas.enabled = value;
}
}
Controller
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//控制器基类
public class BaseController
{
private Dictionary<string,Action<object[]>> message;//事件列表
protected BaseModel model;//模版数据
public BaseController()
{
message = new Dictionary<string, Action<object[]>>();
}
//注册后,调用的初始化函数(要求所有控制器初始化后执行)
public virtual void Init()
{
}
public virtual void OnLoadView(IBaseView view){}//加载视图
//打开视图
public virtual void OpenView(IBaseView view)
{
}
//关闭视图
public virtual void CloseView(IBaseView view)
{
}
//注册模版事件
public void RegisterFunc(string eventName,Action<object[]> callback)
{
if(message.ContainsKey(eventName))
{
message[eventName]+=callback;
}
else
{
message.Add(eventName,callback);
}
}
//移除模版事件
public void UnRegisterFunc(string eventName)
{
if(message.ContainsKey(eventName))
{
message.Remove(eventName);
}
}
//触发本模块事件
public void ApplyFunc(string eventName, params object[] args)
{
if(message.ContainsKey(eventName))
{
message[eventName].Invoke(args);
}
else
{
Debug.Log("error:"+eventName);
}
}
//触发其他模板的事件
public void ApplyControllerFunc(int controllerKey,string eventName, params object[] args)
{
GameApp.ControllerManager.ApplyFunc(controllerKey,eventName,args);
}
//
public void ApplyControllerFunc(ControllerType controllerType,string eventName,params object[] args)
{
ApplyControllerFunc((int)controllerType,eventName,args);
}
//设置模型数据
public void SetModel(BaseModel model)
{
this.model = model;
this.model.controller = this;
}
//得到模型数据
public BaseModel GetModel()
{
return model;
}
//得到模型数据泛型
public T GetModel<T>() where T:BaseModel
{
return model as T;
}
//得到指定控制器控制的模型
public BaseModel GetControllerMode(int controllerKey)
{
return GameApp.ControllerManager.GetControllerModel(controllerKey);
}
//删除控制器
public virtual void Destroy()
{
RemoveModuleEvent();
RemoveGlobalEvent();
}
//初始化模板事件
public virtual void InitModuleEvent()
{
}
//移除模板事件
public virtual void RemoveModuleEvent()
{
}
//初始化全局事件
public virtual void InitGlobalEvent()
{
}
//移除全局事件
public virtual void RemoveGlobalEvent()
{
}
}
ViewManager
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//视图信息类
public class ViewInfo
{
public string PrefabName;//视图预制体
public Transform parentTf;//父节点
public BaseController controller;//视图所属控制器
public int sorting_order;//显示层级顺序
}
//视图管理器
public class ViewManager
{
public Transform canvasTf;//画布组件
public Transform worldCanvasTf;//世界画布组件
Dictionary<int,IBaseView> _opens;//开启的视图列表
Dictionary<int,IBaseView> _viewCache;//视图缓存
Dictionary<int,ViewInfo> _views;//注册的视图信息列表
public ViewManager()
{
canvasTf = GameObject.Find("Canvas").transform;
worldCanvasTf = GameObject.Find("WorldCanvas").transform;
_opens = new Dictionary<int, IBaseView>();
_views = new Dictionary<int, ViewInfo>();
_viewCache = new Dictionary<int, IBaseView>();
}
//注册视图信息
public void Register(int key,ViewInfo viewInfo)
{
if(_views.ContainsKey(key)==false)
{
//Debug.Log(viewInfo.PrefabName);
_views.Add(key,viewInfo);
}
}
//注册视图信息
public void Register(ViewType viewType,ViewInfo viewInfo)
{
Register((int)viewType,viewInfo);
}
//移除视图信息
public void UnRegister(int key)
{
if(_views.ContainsKey(key))
{
_views.Remove(key);
}
}
//移除面板
public void RemoveView(int key)
{
_views.Remove(key);
_viewCache.Remove(key);
_opens.Remove(key);
}
//移除控制器中的面板视图
public void RemoveViewByController(BaseController controller)
{
foreach(var item in _views)
{
if(item.Value.controller == controller)
{
RemoveView(item.Key);
}
}
}
//指定视图是否打开
public bool IsOpen(int key)
{
return _opens.ContainsKey(key);
}
//获得指定视图
public IBaseView GetView(int key)
{
if(_opens.ContainsKey(key))
{
return _opens[key];
}
if(_viewCache.ContainsKey(key))
{
return _viewCache[key];
}
return null;
}
//获得指定视图,泛型
public T GetView<T>(int key) where T:class,IBaseView
{
IBaseView view = GetView(key);
if(view!=null)
{
return view as T;
}
return null;
}
//销毁指定视图
public void Destroy(int key)
{
IBaseView oldView = GetView(key);
if(oldView!=null)
{
UnRegister(key);
oldView.DestroyView();
_viewCache.Remove(key);
}
}
//关闭面板视图
public void Close(int key,params object[] args)
{
Debug.Log(key);
if(IsOpen(key)==false)
{
Debug.Log("界面不存在");
return;
}
IBaseView view = GetView(key);
if(view!=null)
{
_opens.Remove(key);
view.Close(args);
_viewCache[key].Controller.CloseView(view);
}
}
//打开指定视图面板
public void Open(ViewType type,params object[] args)
{
Open((int)type,args);
}
//打开指定视图面板
public void Open(int key,params object[] args)
{
IBaseView view = GetView(key);
ViewInfo viewInfo = _views[key];
if(view ==null)
{
string type = ((ViewType)key).ToString();
GameObject uiObj = GameObject.Instantiate(Resources.Load($"View/{viewInfo.PrefabName}"),viewInfo.parentTf) as GameObject;
Canvas canvas = uiObj.GetComponent<Canvas>();
if(canvas==null)
{
canvas = uiObj.AddComponent<Canvas>();
}
if(uiObj.GetComponent<GraphicRaycaster>()==null)
{
uiObj.AddComponent<GraphicRaycaster>();
}
//可以设置层级
canvas.overrideSorting = true;
//设置层级
canvas.sortingOrder = viewInfo.sorting_order;
//为视图添加对应脚本
view = uiObj.AddComponent(Type.GetType(type)) as IBaseView;
//设置视图ID;
view.ViewId = key;
//设置控制器
view.Controller = viewInfo.controller;
//添加到视图缓存
_viewCache.Add(key,view);
viewInfo.controller.OnLoadView(view);
}
if(this._opens.ContainsKey(key)==true)
{
return;
}
this._opens.Add(key,view);
//已经初始化过
if(view.IsInit())
{
view.SetVisible(true);
view.Open(args);
viewInfo.controller.OpenView(view);
}
else
{
view.InitUI();
view.InitData();
view.Open(args);
viewInfo.controller.OpenView(view);
}
}
}
这个框架中,Model和View没有直接联系,而是通过Controller来控制Model与View之间的信息通信。BaseController中SetModel让Controller和Model关联,面板在ViewManager注册,而在Register方法中让Controller和View关联。进而让Controller来控制Model与View之间的信息通信。
五、总结
MVC设计模式是一种非常有用的架构模式,它使得应用程序的结构更加清晰和易于维护。通过将数据、用户界面和业务逻辑分离,MVC模式提高了代码的可重用性和可扩展性。但MVC设计模式也有缺陷,由于将逻辑与数据分离,程序员需要了解并设计MVC之间的结构,信息通信方式等等,且这一定程度上让程序更加复杂。