C#的MVC模式和观察者模式的理解和运用

前言:只是自己的一点理解,自己在项目实践中没出现问题,如果有不足欢迎指正。主打听劝!

一:什么是MVC模式

MVC是一种经典的软件架构模式,用于实现用户界面与业务逻辑的分离。它分为三层:

Model:用于数据和业务逻辑层,表示应用的核心结构或数据结构。

View:表现层,负责显示数据,不直接处理业务逻辑。

Controller:控制器层,接收用户输入或数据更新,处理逻辑,通知 Model 和 View。

作用:管理模块分离数据、表现和逻辑

二:什么是观察者模式

观察者模式(Observer Pattern)是一种设计模式,用于实现“发布-订阅”机制: 一个对象(发布者)状态改变时,会通知所有订阅者(观察者); 被广泛应用于事件驱动系统UI 更新数据推送场景。

Subject(被观察者)保存观察者列表,状态改变时通知他们。

Observer(观察者)定义回调接口,接收状态变化。

作用:实现模块间的解耦和通知机制

三:如何协同工作和他们的作用

协同工作

ModelController 可以作为 Subject(被观察者)

View 作为 Observer(观察者),接收数据变化通知,自动更新界面;                             

SocketManager(数据源)
     ↓
DevjosnController(Controller + Subject)
     ↓          ↘
View1(Observer) View2(Observer)

作用:

使用 MVC,把数据处理和 UI 逻辑解耦

使用观察者模式,让多个 UI 界面响应不同控制器的数据变化

易于拓展:你以后要加新的设备、UI 组件,不用改核心数据处理逻辑;

易于维护:UI 问题和通信问题可以独立排查。

四:具体的应用

1.工具类:底层数据接收

这个类负责通讯的连接管理与数据接收,案例是wenbsocket
using System;
using System.Text;
using UnityEngine;
using NativeWebSocket;
using System.Threading.Tasks;
using System.IO;

/// <summary>
/// 负责 WebSocket 的连接管理与数据接收,不处理业务数据逻辑
/// </summary>
public class SocketManager : MonoBehaviour
{
    private WebSocket websocket;

    private int reconnectAttempts = 0;
    private const int maxReconnectAttempts = 10;
    private bool isReconnecting = false;

    private async void Start()
    {
        await ConnectWebSocket();
    }

    /// <summary>
    /// 连接 WebSocket,并注册事件回调
    /// </summary>
    private async Task ConnectWebSocket()
    {
        if (websocket != null)
        {
            await websocket.Close();
            websocket = null;
        }

        websocket = new WebSocket("ws://localhost:5260/ws");

        websocket.OnOpen += () =>
        {
            Debug.Log("✅ WebSocket 连接成功!");
            reconnectAttempts = 0;
            isReconnecting = false;
        };

        websocket.OnError += (e) =>
        {
            Debug.LogError("❌ WebSocket 错误: " + e);
            TryReconnect();
        };

        websocket.OnClose += (e) =>
        {
            Debug.LogWarning("🔌 WebSocket 连接关闭!");
            TryReconnect();
        };

        websocket.OnMessage += (bytes) =>
        {
            string json = Encoding.UTF8.GetString(bytes);

            // 可选调试写入桌面文件
            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            string path = Path.Combine(desktopPath, "jsonData.txt");
            File.WriteAllText(path, json);

            // 转交给 DevjosnController 进行解析和数据分发
            DevjosnController.ProcessJsonData(json);
        };

        try
        {
            Debug.Log("🚀 正在连接 WebSocket...");
            await websocket.Connect();
        }
        catch (Exception ex)
        {
            Debug.LogError("🚫 WebSocket 连接失败:" + ex.Message);
            TryReconnect();
        }
    }

    /// <summary>
    /// 断线重连机制,最多尝试10次
    /// </summary>
    private async void TryReconnect()
    {
        if (isReconnecting || reconnectAttempts >= maxReconnectAttempts)
            return;

        isReconnecting = true;
        reconnectAttempts++;

        Debug.Log($"🔄 正在尝试第 {reconnectAttempts} 次重连...");
        await Task.Delay(3000);
        await ConnectWebSocket();
    }

    void Update()
    {
#if !UNITY_WEBGL || UNITY_EDITOR
        websocket?.DispatchMessageQueue();
#endif
    }

    private async void OnApplicationQuit()
    {
        if (websocket != null)
        {
            await websocket.Close();
        }
    }
}

2.Model:数据结构

这个数据实体类封装了业务中需要使用的数据字段
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;


[Serializable]
public class Device
{
    public string deviceId;
    public string deviceCode;
    public string name;
    public int deviceType;
    public bool isOnline;
    public int? status;
    public string load;
    public float? runningHours;
    public string lastOnlineTime;
    public string lastOfflineTime;
    public string ipAddress;
    public int port;
    public string description;
    public string locationX;
    public string locationY;
    public string locationZ;
    public string manufacturer;
    public string model;
    public string serialNumber;
    public string firmwareVersion;
    public string softwareVersion;
    public bool isEnabled;
    public string createdTime;
    public string updatedTime;
    public string communicationStationId;
    public string faultCode;
    public string faultMessage;
}

[Serializable]
public class DevController
{
    public string id;
    public string code;
    public string name;
    public string type;
    public string location;
    public int status;
    public string ipAddress;
    public int port;
    public string protocol;
    public int sendBufferLength;
    public string receiveBuffer;
    public int receiveBufferLength;
    public int queueCount;
    public bool isActive;
    public bool isOnline;
    public bool isAvailable;
    public string parameters;
    public string sendBuffer;
    public bool isAutoConnect;
    public string createdAt;
    public string updatedAt;
    public string lastHeartbeat;
    public List<Device> devices;
}

[Serializable]
public class ControllerList
{
    public DevController[] Devcontrollers;
    
}

3.Controller:控制层 同时这里还是Subject(被观察者)

这个类是数据处理和推送(具体解析的什么数据我给删除了,解析逻辑还在)
using LitJson;
using System;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// DevjosnController 用于接收设备控制器 JSON 数据,解析后进行分发,
/// 实现全局与局部观察者的事件通知机制(观察者模式)。
/// 单例结构确保全局唯一数据管理点。
/// </summary>
public class DevjosnController : MonoBehaviour
{ 
    private static Dictionary<string, List<IDeviceDataObserver>> controllerObservers = new Dictionary<string, List<IDeviceDataObserver>>(); // 存储按控制器 ID 注册的观察者列表  
    private static List<IDeviceDataObserver> globalObservers = new List<IDeviceDataObserver>(); // 存储所有注册的全局观察
    // 上一次接收到的控制器数据,用于做数据变更判断,防止重复刷新
    private List<DevController> previousData = new List<DevController>();
    // 单例引用,方便全局访问本类
    private static DevjosnController instance;
    /// <summary>
    /// Mono Awake 生命周期中初始化单例引用
    /// </summary>
    void Awake()
    {
        instance = this;
    }

    #region 注册与注销观察者(观察者模式核心逻辑)

    /// <summary>
    /// 注册全局观察者,会接收到所有控制器的数据变动
    /// </summary>
    public static void RegisterObserver(IDeviceDataObserver observer)
    {
        if (!globalObservers.Contains(observer))
            globalObservers.Add(observer);
    }

    /// <summary>
    /// 注销全局观察者
    /// </summary>
    public static void UnregisterObserver(IDeviceDataObserver observer)
    {
        globalObservers.Remove(observer);
    }

    /// <summary>
    /// 注册指定控制器 ID 的观察者,仅接收该控制器的数据变动
    /// </summary>
    public static void RegisterObserver(string controllerId, IDeviceDataObserver observer)
    {
        if (!controllerObservers.ContainsKey(controllerId))
            controllerObservers[controllerId] = new List<IDeviceDataObserver>();
        if (!controllerObservers[controllerId].Contains(observer))
            controllerObservers[controllerId].Add(observer);
    }

    /// <summary>
    /// 注销指定控制器 ID 的观察者
    /// </summary>
    public static void UnregisterObserver(string controllerId, IDeviceDataObserver observer)
    {
        if (controllerObservers.ContainsKey(controllerId))
            controllerObservers[controllerId].Remove(observer);
    }

    #endregion

    /// <summary>
    /// 内部方法:从 SocketManager 推送的 JSON 数据中解析后的 DevController 列表,进行事件分发
    /// </summary>
    private void OnControllersFromSocket(List<DevController> newControllers)
    {
        // 只有在数据真正变化时才触发通知,避免 UI 或逻辑频繁刷新
        if (!IsSameData(newControllers, previousData))
        {
            previousData = new List<DevController>(newControllers);

            // 推送给全局观察者
            foreach (var observer in globalObservers)
                observer.OnDeviceDataUpdated(previousData);

            // 推送给按 controller.id 注册的局部观察者
            foreach (var controller in previousData)
            {
                if (controllerObservers.TryGetValue(controller.id, out var observers))
                {
                    foreach (var observer in observers)
                        observer.OnControllerDataUpdated(controller);
                }
            }
        }
    }

    /// <summary>
    /// 比较两组控制器数据是否完全一致(仅检查 ID 和在线状态),避免重复推送
    /// </summary>
    private bool IsSameData(List<DevController> a, List<DevController> b)
    {
        if (a.Count != b.Count) return false;
        for (int i = 0; i < a.Count; i++)
        {
            if (a[i].id != b[i].id || a[i].isOnline != b[i].isOnline)
                return false;
        }
        return true;
    }

    /// <summary>
    /// 接收原始 JSON 字符串数据,进行解析并转换为 DevController 列表,然后触发数据推送
    /// </summary>
    public static void ProcessJsonData(string json)
    {
        try
        {
            JsonData jsonData = JsonMapper.ToObject(json);
            List<DevController> parsedControllers = new List<DevController>();

            foreach (JsonData controllerData in jsonData["Data"])
            {
                // 解析控制器基本信息
                DevController controller = new DevController
                {
                };

                // 解析该控制器下属设备信息
                if (controllerData["devices"] != null)
                {
                    foreach (JsonData deviceData in controllerData["devices"])
                    {
                        Device device = new Device
                        {
                
                        };
                        controller.devices.Add(device);
                    }
                }

                parsedControllers.Add(controller);
            }

            // 将解析后的控制器列表传递给事件分发函数
            instance.OnControllersFromSocket(parsedControllers);
        }
        catch (Exception ex)
        {
            Debug.LogError("❌ JSON 解析失败:" + ex.Message);
        }
    }
}

4.view作为Observer(被观察者)

这个类接收观察者数据推送通知做对应逻辑处理(具体后续操作删除接收数据逻辑还在)

接收多个控制器

 

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSXanimatormini : MonoBehaviour, IDeviceDataObserver
{
    // ✅ 支持多个目标控制器ID(可以同时监听多个控制器)08小库和连廊   09大库
    private HashSet<string> targetControllerIds = new() {
      
          "0d4fe201-a2d3-40db-9972-4e5194c25108"
    };
  
    /// <summary>
    /// ✅ 组件启用时:注册为数据中心的观察者
    /// </summary>
    private void Start()
    {
       
    }

    /// <summary>
    /// ✅ 组件启用时:确保注册
    /// </summary>
    private void OnEnable()
    {
        DevjosnController.RegisterObserver(this);
    }

    /// <summary>
    /// ✅ 组件禁用时:取消注册,避免内存泄漏
    /// </summary>
    private void OnDisable()
    {
        DevjosnController.UnregisterObserver(this);
    }

    /// <summary>
    /// ✅ 接收单个控制器数据(当前未使用)
    /// </summary>
    void IDeviceDataObserver.OnControllerDataUpdated(DevController controller) { }

    /// <summary>
    /// ✅ 接收所有控制器推送的设备数据,筛选目标控制器,更新输送线状态
    /// </summary>
    public void OnDeviceDataUpdated(List<DevController> controllers)
    {

        foreach (var controller in controllers)
        {
            if (!targetControllerIds.Contains(controller.id))
                continue;

            foreach (var device in controller.devices)
            {
                string load = device.load;
                string deviceCode = device.deviceCode;

                if (string.IsNullOrEmpty(load) || string.IsNullOrEmpty(deviceCode))
                    continue; // 跳过无效数据

                if (int.TryParse(device.status.ToString(), out int status))
                {
                    statusDict[deviceCode] = status;
                    loadDict[deviceCode] = load;
                    GoodsMove();

                }
            }
        }
    }
 }

接收单个控制器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using DG.Tweening;

public class Fourwaycar2 : MonoBehaviour, IDeviceDataObserver
{

    public void OnDeviceDataUpdated(List<DevController> controllers) { } // 取多个控制器留空
    /// <summary>
    /// 观察者事件注册
    /// </summary>
    private void OnEnable()
    {
        DevjosnController.RegisterObserver("22222222-2222-2222-2222-222222222222", this);
    }

    private void OnDisable()
    {
        DevjosnController.UnregisterObserver("22222222-2222-2222-2222-222222222222", this);
    }
    /// <param name="controllers"></param>
    /// <summary>
    /// 接收到控制器ID=1的设备数据后触发
    /// </summary>
    public void OnControllerDataUpdated(DevController controller)
    {  
        //接收控制器数据 
        foreach (var device in controller.devices)
        {
        }
    }
    
}

5:观察者模式接口管理

using System.Collections.Generic;
/// <summary>
/// 观察者模式接口
/// </summary>
public interface IDeviceDataObserver
{
    // 接收所有控制器的更新
    void OnDeviceDataUpdated(List<DevController> controllers);

     接收特定控制器的设备列表带上控制器数据
    void OnControllerDataUpdated(DevController controller);
}

最后多的就不扯淡了,已经注释的非常清楚了,接下来要学框架的应用,等有成果我会在回来的!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值