<码字不易,希望大家点点关注支持一下>
PureMVC框架解读
我们先讲解一下简单事件系统和PureMVC中的命令/通知系统做个比较。
1.简单事件系统
<事件系统是委托的典型用法,C#委托包含Action、delegate、Func、predicate几种类型,具体的用法可以去百度查阅一下其他资料,这里我们先简单讲解一下事件系统。事件系统在Unity中可以用来解耦视图与模型,使得视图和模型重用性都有所提升。Unity WIKI这里有很多变种的事件系统。>
1.1 什么是事件系统
简单讲就是利用字典记录方法,执行事件系统就是调用已经记录的方法。
1.字典记录事件集合
2.执行事件的接口
3.注册事件的接口
4.注销事件的接口
5.清空事件的接口
6.接口通常会有几个重载方法,实现不同的参数数量
1.2 事件系统代码
以下是一个简易事件系统的模板:
public class EventSystem
{
//事件字典
private static readonly Dictionary Events = new Dictionary();
//执行事件的重载方法
public static void Invoke(string eventName)
{
foreach (Delegate @delegate in invoke(eventName))
@delegate.DynamicInvoke();
}
public static void Invoke(string eventName,T argt ){}
//执行事件异常检查
private static Delegate[] invoke( string eventName )
{
if (!Events.ContainsKey(eventName))
UnityEngine.Debug.LogError(string.Format("Can not get the {0} event!",eventName));
Delegate @delegate = Events[eventName];
return @delegate.GetInvocationList();
}
//注册事件的重载方法
public static void Register( string eventName, Action action )
{
register(eventName);
Events[eventName] = (Action)Events[eventName] + action;
}
public static void Register( string eventName , Action action ){}
//注册事件
private static void register( string eventName )
{
if (!Events.ContainsKey(eventName))
Events.Add(eventName, null);
}
//注销事件的重载方法
public static void UnRegister( string eventName , Action action )
{
register(eventName);
Events[eventName] = (Action)Events[eventName] - action;
}
//清楚事件的重载方法
public static void Clear( string eventName )
{
if (Events.ContainsKey(eventName))
Events[eventName] = null;
}
}
1.3 事件系统案例
结合一个小的案例看一下简单的事件系统的使用:
internal class TestClass : MonoBehaviour
{
public const string EVENT_NAME = "EventName";
private void Awake()
{
TestEvent test = new TestEvent();
//注册事件
EventSystem.Register(EVENT_NAME,test.Invoke);
}
}
internal class TestEvent
{
public void Invoke()
{
Debug.Log("Invoke");
}
}
internal class TestInvoker : MonoBehaviour
{
public const string EVENT_NAME = "EventName";
public void Start()
{
//执行事件,即可执行以及注册对应的事件
SpringFramework.Event.EventSystem.Invoke(EVENT_NAME);
}
}
通过以上简述大家应该对简易事件系统有个了解,接下里我们看看PureMVC中的命令/通知系统,功能和简易事件系统一样实现部分代码之间的解耦,让方法调用更加的便捷。
2.PureMVC通知系统
2.1 PureMVC
通知系统与简易事件系统的区别
业务拆分更加细致,通知内容(Notificatoin:INotification),通知发送者(Notifer:INotifer),通知执行者(Observer:IObserver)全部都拆分为具体的类型,使得整个系统的拓展性更强。
Notification作为单独类型可自由定义通知内容,拓展方便简单,简易事件系统拓展会受到参数数量限制,导致拓展复杂。
简易事件系统参数是通过泛型方法来定义的,但是PureMVC中将参数装箱为object类型,然后在执行时拆箱为对应类型,虽然装箱拆箱消耗了一定的性能,但是使得参数传递变得更加简单,方便。
2.2PureMVC通知系统代码分析
通知系统大概拆分为通知发送者(Notifer),通知内容(Notification),通知观察者/执行者(Observer)
2.2.1 PureMVC通知系统核心代码分析
- Notifer:INotifer 发送通知的方法
public interface INotifier
{
//发送通知的重载方法
void SendNotification(string notificationName);
void SendNotification(string notificationName, object body);
void SendNotification(string notificationName, object body, string type);
}
public class Notifier : INotifier
{
//保存Facade的实例,通知通过外观Facade通知给View(外观者保存了MVC三个模块的实例),View记录了所有的观察者,然后遍历观察者找到对应的观察者,通知观察者执行通知
private IFacade m_facade = PureMVC.Patterns.Facade.Instance;
public void SendNotification(string notificationName)
{
this.m_facade.SendNotification(notificationName);
}
public void SendNotification(string notificationName, object body)
{
this.m_facade.SendNotification(notificationName, body);
}
public void SendNotification(string notificationName, object body, string type)
{
this.m_facade.SendNotification(notificationName, body, type);
}
protected IFacade Facade
{
get
{
return this.m_facade;
}
}
}
- Notification:INotification 通知的具体内容
public interface INotification
{
//重写通知ToString,用于调试输出
string ToString();
//通知事件
object Body { get; set; }
//通知名称
string Name { get; }
//通知类型
string Type { get; set; }
}
//Notification只是实现了接口中的内容
public class Notification : INotification
{
private object m_body;
private string m_name;
private string m_type;
public Notification(string name) : this(name, null, null){}
public Notification(string name, object body) : this(name, body, null){}
public Notification(string name, object body, string type)
{
this.m_name = name;
this.m_body = body;
this.m_type = type;
}
public override string ToString()
{
return ((("Notification Name: " + this.Name) + "\nBody:" + ((this.Body == null) ? "null" : this.Body.ToString())) + "\nType:" + ((this.Type == null) ? "null" : this.Type));
}
public object Body
{
get
{
return this.m_body;
}
set
{
this.m_body = value;
}
}
public string Name
{
get
{
return this.m_name;
}
}
public string Type
{
get
{
return this.m_type;
}
set
{
this.m_type = value;
}
}
}
- Observer : IObserver 观察者/执行者,根据通知内容反射得到中介者和命令的方法,然后传参数执行。
public interface IObserver
{
//对比NotifyContext
bool CompareNotifyContext(object obj);
//通知观察者
void NotifyObserver(INotification notification);
//记录是Mediator或Command
object NotifyContext { set; }
//通知方法
string NotifyMethod { set; }
}
public class Observer : IObserver
{
//...其他的字段和方法
public void NotifyObserver(INotification notification)
{
object notifyContext;
lock (this.m_syncRoot)
{
notifyContext = this.NotifyContext;
}
//利用反射获取方法然后执行
Type type = notifyContext.GetType();
//这里设置忽略字母的大小写|公共成员|实例成员
BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
//根据设置的中介者的名字或者是命令的名字执行对应的方法
//如果notifyContext是中介者(Mediator)方法名是HandleNotification
//notifyContext是命令方法名是ExecuteCommand
//HandleNotification和ExecuteCommand在注册中介者和命令时构造的Observer的名字为notifyContext或HandleNotification
MethodInfo method = type.GetMethod(this.NotifyMethod, bindingAttr);
method.Invoke(notifyContext , new object[] { notification });
}
}
2.2.2 PureMVC通知系统与MVC结合代码分析
- 注册
<因为观察者/执行者是在注册中介者或者是命令的时候构造并存入字典的,在View.RegisterMediator方法中构造观察者并写入字典,命令的注册时在Controller中执行的,Controller又通过View的实例调用View中的RegisterObserver注册命令的观察者/执行者>
public class View : IView
{
protected IDictionary m_mediatorMap = new Dictionary();
//观察者字典
protected IDictionary> m_observerMap = new Dictionary>();
public virtual void RegisterMediator(IMediator mediator)
{
lock (this.m_syncRoot)
{
if (this.m_mediatorMap.ContainsKey(mediator.MediatorName))
{
return;
}
this.m_mediatorMap[mediator.MediatorName] = mediator;
//获取中介者的通知列表
IList list = mediator.ListNotificationInterests();
if (list.Count > 0)
{
IObserver observer = new Observer("handleNotification", mediator);
for (int i = 0; i < list.Count; i++)
{
//将通知名注册给观察者
this.RegisterObserver(list[i].ToString(), observer);
}
}
}
mediator.OnRegister();
}
public virtual void RegisterObserver(string notificationName, IObserver observer)
{
lock (this.m_syncRoot)
{
if (!this.m_observerMap.ContainsKey(notificationName))
{
//字典key存储通知名称 value存储观察者
this.m_observerMap[notificationName] = new List();
}
this.m_observerMap[notificationName].Add(observer);
}
}
}
//命令的注册
public class Controller : IController
{
// 记录命令的类型
protected IDictionary m_commandMap = new Dictionary();
protected IView m_view;
public virtual void RegisterCommand(string notificationName, Type commandType)
{
lock (this.m_syncRoot)
{
if (!this.m_commandMap.ContainsKey(notificationName))
{
this.m_view.RegisterObserver(notificationName, new Observer("executeCommand", this));
}
this.m_commandMap[notificationName] = commandType;
}
}
}
- 执行
<通过Notifer(执行者)我们知道通知的发送是通过Facade.m_view.NotifyObservers()方法发出的>
public class View : IView
{
public virtual void NotifyObservers(INotification notification)
{
IList list = null;
lock (this.m_syncRoot)
{
if (this.m_observerMap.ContainsKey(notification.Name))
{
IList collection = this.m_observerMap[notification.Name];
//获取到通知已经注册的所有观察者
list = new List(collection);
}
}
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
//遍历观察者并执行观察者中的方法,通过反射获取方法执行HandleNotification或者ExecuteCommnd
//ExecuteCommand是Controller中的方法,它会遍历所有的命令类型找到对应的命令然后执行Execute方法
list[i].NotifyObserver(notification);
}
}
}
}
<执行的方法类似以下:>
public class ClientMediator : Mediator
{
public override void HandleNotification(INotification notification)
{
switch (notification.Name)
{
case OrderSystemEvent.CALL_WAITER:
ClientItem client = notification.Body as ClientItem;
if(null == client)
throw new Exception("对应桌号顾客不存在,请核对!");
Debug.Log(client.id + " 号桌顾客呼叫服务员 , 索要菜单 ");
break;
case OrderSystemEvent.ORDER:
Order order1 = notification.Body as Order;
if(null == order1)
throw new Exception("order1 is null ,please check it!");
order1.client.state++;
View.UpdateState(order1.client);
break;
case OrderSystemEvent.PAY:
Order finishOrder = notification.Body as Order;
if ( null == finishOrder )
throw new Exception("finishOrder is null ,please check it!");
finishOrder.client.state++;
View.UpdateState(finishOrder.client);
SendNotification(OrderSystemEvent.GET_PAY, finishOrder);
break;
}
}
}
internal class StartUpCommand : SimpleCommand
{
public override void Execute(INotification notification)
{
//菜单代理
MenuProxy menuProxy = new MenuProxy();
Facade.RegisterProxy(menuProxy);
//客户端代理
ClientProxy clientProxy = new ClientProxy();
Facade.RegisterProxy(clientProxy);
//服务员代理
WaiterProxy waitProxy = new WaiterProxy();
Facade.RegisterProxy(waitProxy);
//厨师代理
CookProxy cookProxy = new CookProxy();
Facade.RegisterProxy(cookProxy);
OrderProxy orderProxy = new OrderProxy();
Facade.RegisterProxy(orderProxy);
MainUI mainUI = notification.Body as MainUI;
if(null == mainUI)
throw new Exception("程序启动失败..");
Facade.RegisterMediator(new MenuMediator(mainUI.MenuView));
Facade.RegisterMediator(new ClientMediator(mainUI.ClientView));
Facade.RegisterMediator(new WaiterMediator(mainUI.WaitView));
Facade.RegisterMediator(new CookMediator(mainUI.CookView));
}
}
2.2.3 PureMVC通知系统代码总结
PureMVC中通知的执行分为两种:中介者(Mediator)和具体的命令(Command),中介者是面向视图(View)的执行者,调用INotification.HandleNotification方法来执行具体的操作,命令是面向控制器(Controller)的执行者,调用Execute来执行具体的操作,本质是一样的,但是可以区分一下两只的使用环境,中介者用于视图方面的通知和其他中介者之间的交互,但是命令应该用于系统功能级别,比如启动程序,或者是关闭程序等
PureMVC中通过反射获取观察者的类型来区分中介者、命令这两种不同的通知类型
PureMVC框架总结
通过上一篇讲解核心MVC类和这一篇通知系统的讲解,大家应该对PureMVC有了一个大概的理解,通过看我Github的案例代码,应该就可以入手PureMVC框架了,下面做一个PureMVC的总结。
- PureMVC是一个轻量级架构,但是它却可以有效解耦,提供编码效率,提升部分代码重用
- PureMVC对于超小型项目可能会导致代码过于繁琐,但是只要是团队开发,PureMVC可以帮你避免掉很多不规范
- PureMVC也是一种较为容易理解运行机制的框架,即便是新手也可以很快入门,在团队中还是值得使用的
文章转自CSDN
原文链接:https://blog.csdn.net/qq_29579137/article/details/73717882