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.集中版本控制 所有的版本数据都存在服务器上,用户的本地只有自己以前所同步的版本,如果不连网的话,用户就看不…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
