不懂PureMVC框架問題辆床?深入解讀看完必會(下)

<碼字不易,希望大家點點關注支持一下>

PureMVC框架解讀

我們先講解一下簡單事件系統(tǒng)和PureMVC中的命令/通知系統(tǒng)做個比較送讲。

1.簡單事件系統(tǒng)

<事件系統(tǒng)是委托的典型用法死宣,C#委托包含Action、delegate碴开、Func毅该、predicate幾種類型,具體的用法可以去百度查閱一下其他資料潦牛,這里我們先簡單講解一下事件系統(tǒng)眶掌。事件系統(tǒng)在Unity中可以用來解耦視圖與模型,使得視圖和模型重用性都有所提升。Unity WIKI這里有很多變種的事件系統(tǒng)巴碗。>

1.1 什么是事件系統(tǒng)

簡單講就是利用字典記錄方法,執(zhí)行事件系統(tǒng)就是調(diào)用已經(jīng)記錄的方法朴爬。

1.字典記錄事件集合
2.執(zhí)行事件的接口
3.注冊事件的接口
4.注銷事件的接口
5.清空事件的接口
6.接口通常會有幾個重載方法,實現(xiàn)不同的參數(shù)數(shù)量

1.2 事件系統(tǒng)代碼

以下是一個簡易事件系統(tǒng)的模板:

public class EventSystem
{
    //事件字典
    private static readonly Dictionary<string , Delegate> Events = new Dictionary<string, Delegate>();   

    //執(zhí)行事件的重載方法
    public static void Invoke(string eventName)
    {
            foreach (Delegate @delegate in invoke(eventName))
                @delegate.DynamicInvoke();
    }
    public static void Invoke<T>(string eventName,T argt ){}

    //執(zhí)行事件異常檢查
    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 事件系統(tǒng)案例

結合一個小的案例看一下簡單的事件系統(tǒng)的使用:

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()
    {
        //執(zhí)行事件橡淆,即可執(zhí)行以及注冊對應的事件
        SpringFramework.Event.EventSystem.Invoke(EVENT_NAME);
    }
}
image.png

通過以上簡述大家應該對簡易事件系統(tǒng)有個了解召噩,接下里我們看看PureMVC中的命令/通知系統(tǒng)母赵,功能和簡易事件系統(tǒng)一樣實現(xiàn)部分代碼之間的解耦,讓方法調(diào)用更加的便捷具滴。

2.PureMVC通知系統(tǒng)

2.1 PureMVC

通知系統(tǒng)與簡易事件系統(tǒng)的區(qū)別

  • 業(yè)務拆分更加細致凹嘲,通知內(nèi)容(Notificatoin:INotification),通知發(fā)送者(Notifer:INotifer)构韵,通知執(zhí)行者(Observer:IObserver)全部都拆分為具體的類型周蹭,使得整個系統(tǒng)的拓展性更強。

  • Notification作為單獨類型可自由定義通知內(nèi)容疲恢,拓展方便簡單凶朗,簡易事件系統(tǒng)拓展會受到參數(shù)數(shù)量限制,導致拓展復雜显拳。

  • 簡易事件系統(tǒng)參數(shù)是通過泛型方法來定義的棚愤,但是PureMVC中將參數(shù)裝箱為object類型,然后在執(zhí)行時拆箱為對應類型萎攒,雖然裝箱拆箱消耗了一定的性能遇八,但是使得參數(shù)傳遞變得更加簡單,方便耍休。

2.2PureMVC通知系統(tǒng)代碼分析

通知系統(tǒng)大概拆分為通知發(fā)送者(Notifer),通知內(nèi)容(Notification),通知觀察者/執(zhí)行者(Observer)

2.2.1 PureMVC通知系統(tǒng)核心代碼分析

  • Notifer:INotifer 發(fā)送通知的方法
public interface INotifier
{
    //發(fā)送通知的重載方法
    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記錄了所有的觀察者,然后遍歷觀察者找到對應的觀察者羊精,通知觀察者執(zhí)行通知
    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 通知的具體內(nèi)容
public interface INotification
{
    //重寫通知ToString,用于調(diào)試輸出
    string ToString();
    //通知事件
    object Body { get; set; }
    //通知名稱
    string Name { get; }
    //通知類型 
    string Type { get; set; }
}
//Notification只是實現(xiàn)了接口中的內(nèi)容
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 觀察者/執(zhí)行者斯够,根據(jù)通知內(nèi)容反射得到中介者和命令的方法,然后傳參數(shù)執(zhí)行喧锦。
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;
        }
        //利用反射獲取方法然后執(zhí)行
        Type type = notifyContext.GetType();
        //這里設置忽略字母的大小寫|公共成員|實例成員
        BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
        //根據(jù)設置的中介者的名字或者是命令的名字執(zhí)行對應的方法
        //如果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通知系統(tǒng)與MVC結合代碼分析
  • 注冊
    <因為觀察者/執(zhí)行者是在注冊中介者或者是命令的時候構造并存入字典的读规,在View.RegisterMediator方法中構造觀察者并寫入字典,命令的注冊時在Controller中執(zhí)行的燃少,Controller又通過View的實例調(diào)用View中的RegisterObserver注冊命令的觀察者/執(zhí)行者>
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;
        }
    }
}
  • 執(zhí)行
    <通過Notifer(執(zhí)行者)我們知道通知的發(fā)送是通過Facade.m_view.NotifyObservers()方法發(fā)出的>
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];
                //獲取到通知已經(jīng)注冊的所有觀察者
                list = new List<IObserver>(collection);
            }
        }
        if (list != null)
        {
            for (int i = 0; i < list.Count; i++)
            {
                //遍歷觀察者并執(zhí)行觀察者中的方法束亏,通過反射獲取方法執(zhí)行HandleNotification或者ExecuteCommnd
                //ExecuteCommand是Controller中的方法,它會遍歷所有的命令類型找到對應的命令然后執(zhí)行Execute方法
                list[i].NotifyObserver(notification);
            }
        }
    }
}

<執(zhí)行的方法類似以下:>

 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通知系統(tǒng)代碼總結

  • PureMVC中通知的執(zhí)行分為兩種:中介者(Mediator)和具體的命令(Command),中介者是面向視圖(View)的執(zhí)行者阳液,調(diào)用INotification.HandleNotification方法來執(zhí)行具體的操作怕敬,命令是面向控制器(Controller)的執(zhí)行者,調(diào)用Execute來執(zhí)行具體的操作,本質(zhì)是一樣的帘皿,但是可以區(qū)分一下兩只的使用環(huán)境东跪,中介者用于視圖方面的通知和其他中介者之間的交互,但是命令應該用于系統(tǒng)功能級別,比如啟動程序虽填,或者是關閉程序等

  • PureMVC中通過反射獲取觀察者的類型來區(qū)分中介者丁恭、命令這兩種不同的通知類型

PureMVC框架總結

通過上一篇講解核心MVC類和這一篇通知系統(tǒng)的講解,大家應該對PureMVC有了一個大概的理解卤唉,通過看我Github的案例代碼涩惑,應該就可以入手PureMVC框架了,下面做一個PureMVC的總結桑驱。

  • PureMVC是一個輕量級架構竭恬,但是它卻可以有效解耦,提供編碼效率熬的,提升部分代碼重用
  • PureMVC對于超小型項目可能會導致代碼過于繁瑣痊硕,但是只要是團隊開發(fā),PureMVC可以幫你避免掉很多不規(guī)范
  • PureMVC也是一種較為容易理解運行機制的框架押框,即便是新手也可以很快入門岔绸,在團隊中還是值得使用的

文章轉(zhuǎn)自CSDN
原文鏈接:https://blog.csdn.net/qq_29579137/article/details/73717882

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市橡伞,隨后出現(xiàn)的幾起案子盒揉,更是在濱河造成了極大的恐慌,老刑警劉巖兑徘,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刚盈,死亡現(xiàn)場離奇詭異,居然都是意外死亡挂脑,警方通過查閱死者的電腦和手機藕漱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來崭闲,“玉大人肋联,你說我怎么就攤上這事〉蠹螅” “怎么了橄仍?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長牍戚。 經(jīng)常有香客問我沙兰,道長,這世上最難降的妖魔是什么翘魄? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮舀奶,結果婚禮上暑竟,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好但荤,可當我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布罗岖。 她就那樣靜靜地躺著,像睡著了一般腹躁。 火紅的嫁衣襯著肌膚如雪桑包。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天纺非,我揣著相機與錄音哑了,去河邊找鬼。 笑死烧颖,一個胖子當著我的面吹牛弱左,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炕淮,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拆火,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涂圆?” 一聲冷哼從身側響起们镜,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎润歉,沒想到半個月后模狭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡卡辰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年胞皱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片九妈。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡反砌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萌朱,到底是詐尸還是另有隱情宴树,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布晶疼,位于F島的核電站酒贬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏翠霍。R本人自食惡果不足惜锭吨,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寒匙。 院中可真熱鬧零如,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痊土,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工拦赠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壁榕。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓矛紫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牌里。 傳聞我的和親對象是個殘疾皇子颊咬,可洞房花燭夜當晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容