发布时间:2025-12-10 11:39:01 浏览次数:1
我们先讲解一下简单事件系统和PureMVC中的命令/通知系统做个比较。
事件系统是委托的典型用法,C#委托包含Action、delegate、Func、predicate几种类型,具体的用法可以去百度查阅一下其他资料,这里我们先简单讲解一下事件系统。事件系统在Unity中可以用来解耦视图与模型,使得视图和模型重用性都有所提升。Unity
WIKI这里有很多变种的事件系统。
简单讲就是利用字典记录方法,执行事件系统就是调用已经记录的方法。
字典记录事件集合
执行事件的接口
注册事件的接口
注销事件的接口
清空事件的接口
接口通常会有几个重载方法,实现不同的参数数量
以下是一个简易事件系统的模板:
public class EventSystem{//事件字典private static readonly Dictionary<string , Delegate> Events = new Dictionary<string, Delegate>(); //执行事件的重载方法public static void Invoke(string eventName){foreach (Delegate @delegate in invoke(eventName))@delegate.DynamicInvoke();}public static void Invoke<T>(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<T>( string eventName , Action<T> 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;}}结合一个小的案例看一下简单的事件系统的使用:
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中的命令/通知系统,功能和简易事件系统一样实现部分代码之间的解耦,让方法调用更加的便捷。
业务拆分更加细致,通知内容(Notificatoin:INotification),通知发送者(Notifer:INotifer),通知执行者(Observer:IObserver)全部都拆分为具体的类型,使得整个系统的拓展性更强
Notification作为单独类型可自由定义通知内容,拓展方便简单,简易事件系统拓展会受到参数数量限制,导致拓展复杂
简易事件系统参数是通过泛型方法来定义的,但是PureMVC中将参数装箱为object类型,然后在执行时拆箱为对应类型,虽然装箱拆箱消耗了一定的性能,但是使得参数传递变得更加简单,方便
通知系统大概拆分为通知发送者(Notifer),通知内容(Notification),通知观察者/执行者(Observer)
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{//对比NotifyContextbool CompareNotifyContext(object obj);//通知观察者void NotifyObserver(INotification notification);//记录是Mediator或Commandobject 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或HandleNotificationMethodInfo method = type.GetMethod(this.NotifyMethod, bindingAttr);method.Invoke(notifyContext , new object[] { notification });}}注册
因为观察者/执行者是在注册中介者或者是命令的时候构造并存入字典的,在View.RegisterMediator方法中构造观察者并写入字典,命令的注册时在Controller中执行的,Controller又通过View的实例调用View中的RegisterObserver注册命令的观察者/执行者
public class View : IView{protected IDictionary<string, IMediator> m_mediatorMap = new Dictionary<string, IMediator>();//观察者字典protected IDictionary<string, IList<IObserver>> m_observerMap = new Dictionary<string, IList<IObserver>>();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<string> 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<IObserver>();}this.m_observerMap[notificationName].Add(observer);}}}//命令的注册public class Controller : IController{// 记录命令的类型protected IDictionary<string, Type> m_commandMap = new Dictionary<string, Type>();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<IObserver> list = null;lock (this.m_syncRoot){if (this.m_observerMap.ContainsKey(notification.Name)){IList<IObserver> collection = this.m_observerMap[notification.Name];//获取到通知已经注册的所有观察者list = new List<IObserver>(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));}}PureMVC中通知的执行分为两种:中介者(Mediator)和具体的命令(Command),中介者是面向视图(View)的执行者,调用INotification.HandleNotification方法来执行具体的操作,命令是面向控制器(Controller)的执行者,调用Execute来执行具体的操作,本质是一样的,但是可以区分一下两只的使用环境,中介者用于视图方面的通知和其他中介者之间的交互,但是命令应该用于系统功能级别,比如启动程序,或者是关闭程序等
PureMVC中通过反射获取观察者的类型来区分中介者、命令这两种不同的通知类型