[Unity]PureMVC框架解读(下)
来源:互联网 发布:mac 视频特效 编辑:程序博客网 时间:2024/05/22 16:58
PureMVC框架解读
我们先讲解一下简单事件系统和PureMVC中的命令/通知系统做个比较。
1.简单事件系统
事件系统是委托的典型用法,C#委托包含Action、delegate、Func、predicate几种类型,具体的用法可以去百度查阅一下其他资料,这里我们先简单讲解一下事件系统。事件系统在Unity中可以用来解耦视图与模型,使得视图和模型重用性都有所提升。Unity WIKI这里有很多变种的事件系统。
1.1 什么是事件系统
简单讲就是利用字典记录方法,执行事件系统就是调用已经记录的方法。
- 字典记录事件集合
- 执行事件的接口
- 注册事件的接口
- 注销事件的接口
- 清空事件的接口
- 接口通常会有几个重载方法,实现不同的参数数量
1.2 事件系统代码
以下是一个简易事件系统的模板:
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; }}
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<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)); }}
2.2.3 PureMVC通知系统代码总结
PureMVC中通知的执行分为两种:中介者(Mediator)和具体的命令(Command),中介者是面向视图(View)的执行者,调用INotification.HandleNotification方法来执行具体的操作,命令是面向控制器(Controller)的执行者,调用Execute来执行具体的操作,本质是一样的,但是可以区分一下两只的使用环境,中介者用于视图方面的通知和其他中介者之间的交互,但是命令应该用于系统功能级别,比如启动程序,或者是关闭程序等
PureMVC中通过反射获取观察者的类型来区分中介者、命令这两种不同的通知类型
PureMVC框架总结
通过上一篇讲解核心MVC类和这一篇通知系统的讲解,大家应该对PureMVC有了一个大概的理解,通过看我Github的案例代码,应该就可以入手PureMVC框架了,下面做一个PureMVC的总结。
- PureMVC是一个轻量级架构,但是它却可以有效解耦,提供编码效率,提升部分代码重用
- PureMVC对于超小型项目可能会导致代码过于繁琐,但是只要是团队开发,PureMVC可以帮你避免掉很多不规范
- PureMVC也是一种较为容易理解运行机制的框架,即便是新手也可以很快入门,在团队中还是值得使用的下一次我将解读StrangeIOC控制反转,依赖注入的框架,其中内容更值得我们学习,想要了解的同学可以关注我哦!
Unity自定义UI组件系列
- Unity自定义UI组件(六)日历、日期拾取器
- Unity自定义组件之(五) 目录树 UITree
- Unity自定义UI组件(四)双击按钮、长按按钮
- Unity自定义UI组件(三)饼图篇
- Unity自定义UI组件(二)函数图篇(下)
- Unity自定义UI组件(一)函数图篇(上)
Unity框架解读系列
- [Unity]PureMVC框架解读(上)
分享地址
- Github :https://github.com/ll4080333/PureMVCProject
- CSDN : http://blog.csdn.net/qq_29579137
如果你想了解unity框架的更多知识,欢迎关注我的博客,我会持续更新,支持一下我这个博客新手。如果以上文章对你有帮助,点个赞,让更多的人看到这篇文章我们一起学习。如果有什么指点的地方欢迎在评论区留言,秒回复。
- [Unity]PureMVC框架解读(下)
- [Unity]PureMVC框架解读(上)
- 解读PureMVC框架
- [Unity框架]PureMVC基础
- [Unity框架]PureMVC基础
- [Unity框架]PureMVC在unity中的简单使用
- [Unity框架]PureMVC在unity中的简单使用
- [Unity框架]PureMVC在unity中的简单使用
- Unity编程笔录--ulua+PureMVC框架简单热更新使用
- Unity开发小型游戏中如何便捷使用PureMVC框架
- Unity开发小型游戏中如何便捷使用PureMVC框架
- Unity开发小型游戏中如何便捷使用PureMVC框架
- UNITY之PureMvc
- UNITY之PureMvc基础知识
- 在Unity使用PureMVC
- 初探PureMVC框架
- PureMVC框架知识介绍
- flash框架---PureMvc 实践
- HTTP详解--HTTP方法(五)
- Java面试题
- 石子问题
- Xcode8使用出现 Class PLBuildVersion is implemented in both /Applications/Xcode.app/Contents/Developer/P
- es6学习-2
- [Unity]PureMVC框架解读(下)
- check exception , runtime exception
- c语言---const搭配指针问题
- 从源程序到可执行程序
- 【Python】pip install windows下报ascii无法编码
- JavaWeb项目,使用ajax进行访问Url,依然请求是上个请求地址
- [5] Spring中的AOP操作(使用xml 配置文件的方式)
- leetcode 495. Teemo Attacking
- makefile 工具基本使用(2)