MVC设计模式
在当今的软件开发领域,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; }//面板cavansprotected 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;}//初始化UIpublic 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;}//查找面板中的对象public GameObject Find(string name){if(_ObjCache.ContainsKey(name)){return _ObjCache[name];}_ObjCache.Add(name,transform.Find(name).gameObject);return _ObjCache[name];}public T Find<T>(string name) where T:Component{GameObject obj = Find(name);return obj.GetComponent<T>();}
}
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之间的结构,信息通信方式等等,且这一定程度上让程序更加复杂。
相关文章:
MVC设计模式
在当今的软件开发领域,MVC(Model-View-Controller)设计模式已经成为了一种广泛使用的架构模式。它为应用程序提供了一种结构化的方法,将数据、用户界面和业务逻辑分开,从而使得应用程序更易于维护、扩展和重用。 一、…...
WSL (2103) ERROR: CreateProcessEntryCommon:493: chdir 错误解决
[TOC](WSL (2103) ERROR: CreateProcessEntryCommon:493: chdir 错误解决) 1. 错误信息 <3>WSL (2103) ERROR: CreateProcessEntryCommon:493: chdir(/mnt/d/Program Files/PowerShell/7) failed 52. 解决方法 wsl --shutdownwslrefer: https://github.com/microsoft/…...

【二、自动化测试】为什么要做自动化测试?哪种项目适合做自动化?
自动化测试是一种软件测试方法,通过编写和使用自动化脚本和工具,以自动执行测试用例并生成结果。 自动化旨在替代手动测试过程,提高测试效率和准确性。 自动化测试可以覆盖多种测试类型,包括功能测试、性能测试、安全测试等&…...
用ChatGPT来造一个ChatGPT:计算机领域智能问答系统实践(2)
在PHP语言中,你可以使用MySQL数据库来存储知识库,并使用PHP来实现系统的逻辑。以下是一个简单的示例: 创建数据库表: 首先,创建一个名为 computer_knowledge 的表来存储计算机知识。可以使用以下SQL语句:…...

Ubuntu开机自动挂载硬盘
前言: 因为我的电脑是WIN10 Ubuntu18.04双系统,且两个系统都装在C盘上,而D盘作为数据和代码存储盘,经常会开机就被访问,例如上一次关机前用VS Code访问D盘代码,然后下一次开机的时候打开VSCode发现打不开…...
vue3基础:单文件组件介绍
介绍 Vue 的单文件组件 (即 *.vue 文件,简称 SFC,全称是single file component) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。下面是一个单文件组件的示例: <script> export def…...

OCR字符识别:开始批量识别身份证信息
身份证信息批量识别OCR是一项解决方案,它能够将身份证照片打包成zip格式或通过URL地址进行提交,并能够识别照片中的文本信息。最终,用户可以将识别结果生成为excel文件进行下载。 API接口功能: 1. 批量识别:支持将多…...

php多小区智慧物业管理系统源码带文字安装教程
多小区智慧物业管理系统源码带文字安装教程 运行环境 服务器宝塔面板 PHP 7.0 Mysql 5.5及以上版本 Linux Centos7以上 统计分析以小区为单位,统计如下数据:小区总栋数、小区总户数、小区总人数、 小区租户数量、小区每月收费金额统计、小区车位统计、小…...

解决虚拟机的网络图标不见之问题
在WIN11中,启动虚拟机后,发现网络图标不见了,见下图: 1、打开虚拟机终端 输入“sudo server network-manager stop”,停止网络管理器 输入“cd /回车” , 切换到根目录 输入“cd var回车” ,…...
【Spring类路径Bean定义信息扫描】
Spring类路径Bean定义信息扫描 1. ClassPathBeanDefinitionScanner作用2. 类声明3. 属性4. 构造器5. 扫描方法6. 真正扫描方法7. postProcessBeanDefinition8. 注册bean定义 1. ClassPathBeanDefinitionScanner作用 扫描类路径下的类注册为bean定义。2. 类声明 public class …...

Ubuntu上安装VMware+win11系统手册
Ubuntu安装vmware 下载: Linux 版下载地址:https://www.vmware.com/go/getworkstation-linux 安装: sudo chmod x VMware-Workstation-Full-17.5.0-22583795.x86_64.bundle 执行安装命令: sudo ./VMware-Workstation-Full-17.5.0…...

2024年1月12日:清爽无糖rio留下唇齿之间的香甜
友利奈绪的时间管理 2024年1月12日08:02:28进行java程序设计的上课准备 2024年1月12日08:02:44知道java的题目有18道 2024年1月12日08:43:07随机数去重比较 2024年1月12日08:54:03C语言题目最小公倍数 2024年1月12日08:58:37C语言题目二维数组变一维数组 2024年1月12日10…...

群晖Synology Drive同步文件时过滤指定文件夹“dist“, “node_modules“
群晖Synology Drive同步文件时过滤指定文件夹"dist", “node_modules” mac用户 安装Synology Drive创建同步任务修改Synology Drive配置 打开/Users/[用户名]/Library/Application Support/SynologyDrive/data/session/[同步任务序号,第一个同步任务就…...

小程序中滚动字幕
需求:在录像时需要在屏幕上提示字幕,整体匀速向上滚动 html部分: <view class"subtitles_main"><view style"font-size:34rpx;color: #fff;line-height: 60rpx;" animation"{{animation}}">人生的…...

MySQL中约束是什么?
🎉欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克🍹 ✨博客主页:小小恶斯法克的博客 🎈该系列文章专栏:重拾MySQL 🍹文章作者技术和水平很有限,如果文中出现错误&am…...

若依在表格中如何将字典的键值转为中文
文章目录 一、需求:二、问题解决步骤1、给需要转换的列绑定formatter属性2、获取字典项3、编写formatter属性绑定的方法 一、需求: 后端有时候返回的是字典的键值,在前端展示时需要转成中文值 后端返回的是dictValue,现在要转换…...

用笨办法-刻意练习来提高自己的编程能力
尝试了很多学习方法,企图快速提高编程能力,但最终发现,唯有老老实实刻意练习1,在辛苦与时间积累下,逐渐提升能力,才是最有效的方式。 将自己的笨办法总结了一下,主要包含7个步骤: …...

FineBI报表页面大屏小屏自适应显示问题
大屏正常显示 显示正常 小屏BI自适应显示 存在遮挡字体情况 小屏浏览器缩放显示 等比缩放后显示正常...

NAND Separate Command Address (SCA) 接口命令解读
CA output packet和CA input packet是Separate Command Address (SCA) NAND接口协议中用于命令和地址传输的关键数据结构。 CA Input Packet: 在SCA接口中,输入到NAND器件的命令和地址信息被组织成并行至串行转换的CA(Command and Address)输…...

Git的简单使用说明
Git入门教程 git的最主要的作用:版本控制,协助开发 一.版本控制分类 1.本地版本控制 2.集中版本控制 所有的版本数据都存在服务器上,用户的本地只有自己以前所同步的版本,如果不连网的话,用户就看不…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...