0、写在前面的话
我创建的Unity、C#交流群,有兴趣可加入大家一起学习(人还很少,现在加入就是元老🙀,群里面聊天不限于技术话题):952914223
1、简介
在客户端开发(如 Unity 游戏、桌面程序等)中,MVC 架构是一种经典的分层模式,用于实现逻辑分离、解耦模块、提高可维护性。而在 MVC 的实际落地过程中,命令模式与事件(观察者)模式起到了核心作用。
本文将以实际项目中常见的 MVC 变体结构为例,讲解命令与事件在其中的作用、职责划分,并配以结构图和示例代码。
2、基础概念回顾
1. 命令模式(Command Pattern)
将请求封装为对象,从而使你可以将请求参数化、排队执行或撤销操作。
在 MVC 中用于封装业务逻辑,如购买物品、发起战斗等。
观察者模式(Observer Pattern)
对象之间形成一种一对多的依赖关系,当一个对象状态变化时,依赖它的对象会收到通知并自动更新。
在 MVC 中用于通知 View 层更新 UI,例如金币数变化时刷新显示。
二、命令与事件在 MVC 中的职责划分
我们采用一种精简的 MVC架构:
用户点击 → View → 发送命令 → Command → 更新 Model → 分发事件 → View UI刷新
图示结构:
±---------- + ±------------+ ±-----------+ ±---------+
| View | ----> | Command | -----> | Model | ----> | View |
|(用户操作)| |(业务逻辑) | |(数据变更)| |(刷新UI)|
±----------+ ±------------+ ±-----------+ ±---------+
#3、代码示例(Unity / C#)
以用户购买金币为例,制作简易MVC架构,项目结构:
/Scripts
├── Framework
│ ├── ICommand.cs(命令接口)
│ ├── CommandDispatcher.cs(命令分发)
│ ├── EventSystem.cs(事件系统)
├── Model
│ └── GoldModel.cs(金币model)
├── Command
│ └── BuyGoldCommand.cs(购买金币命令)
├── View
│ └── ShopView.cs(商店页面)
└── Main
└── AppStartup.cs(程序入口)
程序逻辑:
[User点击按钮]
↓
[View] → 调用 dispatcher.Execute(“BuyGold”)
↓
[BuyGoldCommand] → 修改 GoldModel(比如增加200金币)
↓
[GoldModel] → Dispatch(“GoldChanged”, 新值)
↓
[View] ← 监听事件 On(“GoldChanged”) 并更新 UI 显示
✅ 1. ICommand 接口
public interface ICommand
{
void Execute(object data = null);
}
✅ 2. CommandDispatcher 命令调度器
using System.Collections.Generic;
public class CommandDispatcher
{
private Dictionary<string, ICommand> _commandMap = new();
public void Register(string key, ICommand command)
{
_commandMap[key] = command;
}
public void Execute(string key, object data = null)
{
if (_commandMap.TryGetValue(key, out var cmd))
{
cmd.Execute(data);
}
else
{
UnityEngine.Debug.LogWarning($"Command '{key}' not registered.");
}
}
}
✅ 3. EventSystem 简易事件系统
using System;
using System.Collections.Generic;
public static class EventSystem
{
private static Dictionary<string, Action<object>> _events = new();
public static void On(string key, Action<object> listener)
{
if (!_events.ContainsKey(key))
_events[key] = _ => { };
_events[key] += listener;
}
public static void Off(string key, Action<object> listener)
{
if (_events.ContainsKey(key))
_events[key] -= listener;
}
public static void Dispatch(string key, object data = null)
{
if (_events.TryGetValue(key, out var callback))
{
callback?.Invoke(data);
}
}
}
✅ 4. GoldModel 单例模型
public class GoldModel
{
public static GoldModel Instance { get; } = new();
public int Gold { get; private set; } = 1000;
public void AddGold(int amount)
{
Gold += amount;
EventSystem.Dispatch("OnGoldChanged", Gold);
}
}
✅ 5. BuyGoldCommand 命令类
public class BuyGoldCommand : ICommand
{
public void Execute(object data = null)
{
int amount = data is int val ? val : 100;
GoldModel.Instance.AddGold(amount);
}
}
✅ 6. ShopView 示例 View
using UnityEngine;
using UnityEngine.UI;
public class ShopView : MonoBehaviour
{
public Text goldText;
public Button buyButton;
private CommandDispatcher dispatcher;
void Start()
{
dispatcher = new CommandDispatcher();
dispatcher.Register("BuyGold", new BuyGoldCommand());
buyButton.onClick.AddListener(() =>
{
dispatcher.Execute("BuyGold", 200);
});
EventSystem.On("OnGoldChanged", OnGoldChanged);
goldText.text = "Gold: " + GoldModel.Instance.Gold;
}
void OnGoldChanged(object gold)
{
goldText.text = "Gold: " + gold;
}
void OnDestroy()
{
EventSystem.Off("OnGoldChanged", OnGoldChanged);
}
}
✅ 7. AppStartup 启动注册(可选)
如果你要全局注册命令和模型,可以用一个入口类统一初始化:
public class AppStartup : MonoBehaviour
{
public static CommandDispatcher Dispatcher { get; private set; }
void Awake()
{
Dispatcher = new CommandDispatcher();
Dispatcher.Register("BuyGold", new BuyGoldCommand());
}
}