[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);    }}

event

通过以上简述大家应该对简易事件系统有个了解,接下里我们看看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框架的更多知识,欢迎关注我的博客,我会持续更新,支持一下我这个博客新手。如果以上文章对你有帮助,点个赞,让更多的人看到这篇文章我们一起学习。如果有什么指点的地方欢迎在评论区留言,秒回复。

原创粉丝点击