一、基本思想
经典的MVC框架:
- Model(模型):管理数据
- View(视图):管理视图
- Controller(控制器):实现视图与数据之间传递消息
二、关于PureMVC 框架
- Facade 管理 View、Controller、Model 三大类
- Model 管理 Proxy,其中每个Proxy可以负责单个或多个Obj的工作
(Obj与数据相关) - View 管理 Mediator,其中每个Mediator可以负责单个或多个UI的工作
(UI与视图有关) - Controller 负责 Command,实现UI与Obj(即视图与数据)之间的通讯
三、核心组件 Core
3.1 数据 Mdoel
描述:存储IProxy
(中介者),以及对数据的增、删、查操作。
using System.Collections.Generic;
namespace PureMVC
{
/// <summary>
/// 负责保存对Proxy对象的引用
/// </summary>
public class Model:Singleton<Model>
{
/// <summary>
/// 存储 Proxy
/// </summary>
private Dictionary<string, IProxy> proxyMap;
private void Awake()
{
proxyMap = new Dictionary<string, IProxy>();
}
/// <summary>
/// 注册 Proxy
/// </summary>
/// <param name="proxy">数据接口内容</param>
public void RegisterProxy(IProxy proxy)
{
proxyMap[proxy.ProxyName] = proxy;
}
/// <summary>
/// 检索 Proxy
/// </summary>
/// <param name="name">数据接口 名称</param>
public IProxy RetrieveProxy(string name)
{
IProxy proxy = proxyMap.ContainsKey(name) ? proxyMap[name] : null;
return proxy;
}
/// <summary>
/// 删除 Proxy
/// </summary>
/// <param name="name"></param>
public void RemoveProxy(string name)
{
bool exist = proxyMap.ContainsKey(name) ? true : false;
if (exist)
{
proxyMap.Remove(name);
}
}
}
}
Model
作为单例模式,我们希望通过全局直接访问。
3.2 视图 View
描述:存储IMediator
(调节者),以及对视图个人的增、删、查操作。
using System.Collections.Generic;
namespace PureMVC
{
/// <summary>
/// 负责保存对Mediator的引用
/// </summary>
public class View: Singleton<View>
{
/// <summary>
/// 存储 Mediator
/// </summary>
private Dictionary<string, IMediator> mediatorMap;
private void Awake()
{
mediatorMap = new Dictionary<string, IMediator>();
}
/// <summary>
/// 注册 Mediator
/// </summary>
/// <param name="mediator"></param>
public void RegisterMediator(IMediator mediator)
{
mediatorMap[mediator.MediatorName] = mediator;
}
/// <summary>
/// 检索 Mediator
/// </summary>
/// <param name="name"></param>
public IMediator RetrieveMediator(string name)
{
IMediator mediator = mediatorMap.ContainsKey(name) ? mediatorMap[name] : null;
return mediator;
}
/// <summary>
/// 删除 Mediator
/// </summary>
/// <param name="name"></param>
public void RemoveMediator(string name)
{
bool exist = mediatorMap.ContainsKey(name) ? true : false;
if (exist)
{
mediatorMap.Remove(name);
}
}
}
}
View
作为单例模式,我们希望通过全局直接访问。
3.3 消息处理中心 NotificationCenter
描述:存储IObserver
(观察者),以及增、删、查基本操作。
using System.Collections.Generic;
using UnityEngine;
namespace PureMVC
{
/// <summary>
/// 负责保存对所有Command对象的引用
/// </summary>
public class NotificationCenter : Singleton<NotificationCenter>
{
/// <summary>
/// 存储 Observer
/// </summary>
private Dictionary<string, List<IObserver>> observerMap;
private void Awake()
{
observerMap = new Dictionary<string, List<IObserver>>();
}
/// <summary>
/// 添加 Observer
/// 注册 消息接口的信息
/// </summary>
/// <param name="name">消息接口 名称</param>
/// <param name="observer">消息接口 内容</param>
public void AddObserver(string name, IObserver observer)
{
if (!observerMap.ContainsKey(name))
{
observerMap.Add(name, new List<IObserver>());
}
observerMap[name].Add(observer);
}
/// <summary>
/// 添加 Observer(重载)
/// 注册 存储的监听事件信息
/// </summary>
/// <param name="observer">消息接口 内容</param>
public void AddObserver(IObserver observer)
{
string[] list = observer.NotificationList();
for (int i = 0; i < list.Length; i++)
{
AddObserver(list[i], observer);
}
}
/// <summary>
/// 删除 Observer
/// </summary>
/// <param name="name">消息接口 名称</param>
/// <param name="observer">消息接口 内容</param>
public void RemoveObserver(string name, IObserver observer)
{
if (!observerMap.ContainsKey(name)) return;
if (!observerMap[name].Contains(observer)) return;
if (observerMap[name].Count == 0)
observerMap.Remove(name);
else
observerMap[name].Remove(observer);
}
/// <summary>
/// 删除 Observer(重载)
/// </summary>
/// <param name="observer">消息接口内容</param>
public void RemoveObserver(IObserver observer)
{
string[] list = observer.NotificationList();
for (int i = 0; i < list.Length; i++)
{
RemoveObserver(list[i], observer);
}
}
/// <summary>
/// 发送 消息通知
/// </summary>
/// <param name="name">消息接口 名称</param>
/// <param name="data">消息接口类对象数据</param>
public void SendNotification(string name, object data)
{
if (!observerMap.ContainsKey(name))
{
Debug.LogWarning("消息接口:" + name + "不存在");
return;
}
List<IObserver> list = observerMap[name];
for (int i = 0; i < list.Count; i++)
{
list[i].OnNotificationHandler(name, data);
}
}
/// <summary>
/// 打印 监听者列表
/// </summary>
public void PrintReaderMap()
{
}
}
}
NotificationCenter
作为单例模式,我们希望通过全局直接访问。
四、关于TarenaMVC 框架
- PureMVC的精简版本(将Controller精简去)
- 适用于 中型、大型项目
- 改良 - 将PureMVC中的View的一个消息机制强化并单独拿出作为一个消息中心
4.1 调解者接口 IMediator
namespace PureMVC
{
/// <summary>
/// Mediator 视图处理接口
/// </summary>
public interface IMediator
{
/// <summary>
/// 该Mediator的命名
/// </summary>
string MediatorName { get; set; }
}
}
- 任何与视图相关的脚本需要实现接口,以告诉
View
自身属于视图处理,被存储于其内的MediatorMap
中以便更好使用。 MediatorName
:是命名此类调解者的名字,我们通过索引这个来查找接口对象。
4.2 中介者接口 IProxy
namespace PureMVC
{
/// <summary>
/// Proxy 数据处理接口
/// </summary>
public interface IProxy
{
/// <summary>
/// 该Proxy的命名
/// </summary>
string ProxyName { get; set; }
}
}
- 任何与数据信息相关的脚本需要此接口进行实现,以告诉
Model
自身属于数据处理,被存储于其内的ProxyMap
中以便更好使用。 ProxyName
:是命名此类中介者的名字,我们通过索引这个来查找接口对象。
4.3 事件监听(IObserver) 与 消息发送(INotice)
namespace PureMVC
{
/// <summary>
/// 消息监听 接口
/// </summary>
public interface IObserver
{
/// <summary>
/// 消息通知 接收
/// </summary>
/// <param name="name">消息通知 名称</param>
/// <param name="data">消息通知 数据</param>
void OnNotificationHandler(string name, object data);
/// <summary>
/// 监听 存储的消息通知列表
/// </summary>
/// <returns></returns>
string[] NotificationList();
}
}
- 消息内容的监听器,任何来自外部的消息需要此接口进行确认与相应。
- 适用于 视图 类 的变换时接收的指令。
namespace PureMVC
{
/// <summary>
/// 消息通知 接口
/// </summary>
public interface INotice
{
/// <summary>
/// 消息通知 发送
/// </summary>
/// <param name="name"></param>
/// <param name="data"></param>
void SendNotification(string name, object data);
}
}
- 消息发送器:担任传出指令的角色。如控制UI界面的显隐状态,数据信息的请求等。
4.4 IModel 与 IView
- 封装基本的对各自类型的存储
XXXXMap
的访问操作,如增、删、查基本操作。 - 改 一操作:被用于脚本内修改,如
proxyName
或mediatorName
。 - 本文中将这类操作直接添加至
Model
与View
中。
五、封装
描述:封装MVC框架的基本操作,以便统一的引用,而非查找各自的引用方式增加难度。
5.1 入口 Facade
namespace PureMVC
{
/// <summary>
/// 初始化核心层Cores单例
/// </summary>
public class Facade : Singleton<Facade>, INotice
{
/// <summary>
/// 注册 Mediator
/// </summary>
/// <param name="mediator"></param>
public void RegisterMediator(IMediator mediator)
{
View.Instance.RegisterMediator(mediator);
}
/// <summary>
/// 注册 Proxy
/// </summary>
/// <param name="proxy"></param>
public void RegisterProxy(IProxy proxy)
{
Model.Instance.RegisterProxy(proxy);
}
/// <summary>
/// 删除 Mediator
/// </summary>
/// <param name="name"></param>
public void RemoveMediator(string name)
{
View.Instance.RemoveMediator(name);
}
/// <summary>
/// 删除 Proxy
/// </summary>
/// <param name="name"></param>
public void RemoveProxy(string name)
{
Model.Instance.RemoveProxy(name);
}
/// <summary>
/// 检索 Mediator
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IMediator RetrieveMediator(string name)
{
return View.Instance.RetrieveMediator(name);
}
/// <summary>
/// 检索 Proxy
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IProxy RetrieveProxy(string name)
{
return Model.Instance.RetrieveProxy(name);
}
/// <summary>
/// 发送消息
/// </summary>
public void SendNotification(string name,object data)
{
NotificationCenter.Instance.SendNotification(name, data);
}
}
}
Facade
作为单例模式下允许全局访问。- 需要对各数据和视图的
Map
进行操作,所以封装了各自的增、删、查方法。 - 需求发送讯息以通知数据或视图进行相应的响应,故需要
INotice
接口。值得注意的是,消息的发送统一交给Notification
(即 消息中心)处理,而不是分开单独处理。
更多说明:
Facade
如同一个控制器,不需要其他来控制,由我们自主控制,故不需要IObserver
来接收其他的消息。
5.2 中介者 Mediator
namespace PureMVC
{
/// <summary>
/// 负责操作具体组件,改变视图组件状态
/// </summary>
public class Mediator : IMediator, INotice, IObserver
{
/// <summary>
/// 命名
/// </summary>
private const string name = "Mediator";
/// <summary>
/// 命名字段封装属性
/// </summary>
public string MediatorName { get => MediatorName; set => MediatorName = name; }
/// <summary>
/// 消息 发送
/// </summary>
public void SendNotification(string name, object data)
{
Facade.Instance.SendNotification(name, data);
}
/// <summary>
/// 消息 接收
/// </summary>
/// <param name="name">消息名称</param>
/// <param name="data">消息数据</param>
public void OnNotificationHandler(string name, object data)
{
throw new System.NotImplementedException();
}
/// <summary>
/// 监视 已有消息列表
/// </summary>
/// <returns></returns>
public string[] NotificationList()
{
throw new System.NotImplementedException();
}
}
}
- 即 视图层 与 数据层 之间的中介者。
- 这是与视图相关紧密,需要
IMediator
,我们实现了其命名。 - 对视图 与 对数据 的消息发送,需要
INotice
,值得注意的是,此处我们封装了Facade
更加便利于统一发送消息。当然直接使用Notification.Instance.SendNotification(name, data)
也是可行的。只不过出于以一种方式可访问全局变量的目的,请使用Facade
中封装的SendNotification(name, data)
方法。
5.3 调解者 Proxy
namespace PureMVC
{
/// <summary>
/// 处理数据
/// </summary>
public class Proxy : IProxy, INotice
{
/// <summary>
/// 命名
/// </summary>
private const string name = "Proxy";
/// <summary>
/// 命名字段的封装属性
/// </summary>
public string ProxyName { get => ProxyName; set => ProxyName = name; }
/// <summary>
/// 消息 发送
/// </summary>
public void SendNotification(string name, object data)
{
Facade.Instance.SendNotification(name, data);
}
}
}
- 即 对数据的处理。如获取SQLite数据库数据,如英雄名、技能名、CD、生命值等信息。因此需求
IProxy
接口,以更好在ProxyMap
索引和处理。 - 有了数据,则需要通知视图做出相应的表现,自然需要
INotice
接口。
更多说明:
为什么Proxy
不需要IObserver
接口以接受外部消息?Proxy
的职责是处理数据,如读取数据,修改数据,记录数据等。这一行为是独立进行,不需要别人告诉他"嘿!我UI变了,你得给我数据了!",而是别人"我的视图变了,我看看他提供了哪些数据索引或调整方法来使用。