對TypeScript版PureMVC的優化

官網位置:http://puremvc.org/

按接口逐一介紹所作的優化內容吧,沒列舉的亦可能有改動過,但未作大的改動。

1. ICommand

原版command中execute方法接受的參數是INotification對象,從INotification中可以獲取到body, name和type,這種封裝導致puremvc在派發消息時,你需要不停地去組裝和拆分消息,有點繁瑣,所以我將execute的參數改爲了不定參數,可傳可不傳,如果sendNotification時傳遞了參數,則execute可以直接接受真實的形參而非是從一個被封裝的INotification對象中去取Body然後再按鍵值去獲取消息數據

module puremvc {

    export interface ICommand extends INotifier {
        execute(...args: Array<any>): void;
    }
}

// for instance

class TestCommand extends puremvc.SimpleCommand {

    execute(x:number, y:number, z:number):void {
        console.log("x:" + x, "y:" + y, "z:" + z);
    }
}

puremvc.Facade.getInstance().sendNotification("TestCommand", [1, 2, 3]);

// for one argument
puremvc.Facade.getInstance().sendNotification("TestCommand", 0);
// or 
puremvc.Facade.getInstance().sendNotification("TestCommand", [0]);
// both ok

2. INotification

這玩意兒被我刪了,反正沒啥用

3. IMediator

原版puremvc中,View 是通過 listNotificationInterests 方法獲取 mediator 關心的消息列表,然後在命令被派發時,通過調用mediator 的 handleNotification 來響應消息,而 handleNotification 中,則必須通過 if else 或 switch 的格式來響應消息,這個機制也太繁瑣,所以我對它也進行了重構

我在 mediator 中定義了 notificationInterests:Array<IObserver> 屬性,mediator 被註冊時,依然會調用 listNotificationInterests 方法,但這個方法並不返回命令列表,取而代之的是,是在這個方法中,可直接調用 handleNotification 來註冊命令和回調,handleNotification 會調用 registerObserver 來註冊觀察者,觀察者信息會被保存到 notificationInterests 屬性中,我增加了一個 removeNotificationInterests 方法,它在 mediator 被移除時將會調用,被調用時會自動註銷所有在 listNotificationInterests 中通過 handleNotification 註冊的事件

這個改動終結了 handlerNotification 中 if else 或 switch 的寫法,方便了不少,下面是簡單的例子

// register mediator

class TestMediator extends puremvc.Mediator {

    // 注意返回類型是 void 了
    listNotification():void {
        this.handlerNotification("TestMessageA", this.$onTestMessageA);
        this.handlerNotification("TestMessageB", this.$onTestMessageB);
    }

    // 響應 TestMessageA
    private $onTestMessageA(x:number, y:number, z:number):void {
        console.log("x:" + x, "y:" + y, "z:" + z);
    }

    // 響應 TestMessageB
    private $onTestMessageB(a:string):void {
        console.log("a:" + a);
    }

    // 你不需要重寫 removeNotificationInterests 方法,它自己會在 onRemove 時幫你移除回調
}

// send messages
puremvc.Facade.getInstance().sendNotification("TestMessageA", [1, 2, 3]);
puremvc.Facade.getInstance().sendNotification("TestMessageB", "yes");

// 不需要寫 if else 和 switch 方便多了吧?

4. IView

由於 INotification ,ICommand 和 IMediator 的改動,所以這個類的重構比較大,總的來說有以下幾點

a: registerObserver 方法

這個方法原本接受的參數有兩個,1是notificationName:string, 2是observer:IObserver ,改動後則接受5個參數

/**
 * 首先,參數的寫法是參考的flash as3中的EventDispatcher,沒辦法,as3的事件太好用了
 * @receiveOnce: 是否只響應一次,默認爲false
 * @priority: 優先級,優先響應級別高的消息,值越大,級別越高,默認爲1,有這個參數的存在,若你想控制
 * 回調函數的執行順序,則會非常容易
 */
registerObserver(name: string, method: Function, caller: Object, receiveOnce: boolean = false, priority: number = 1): IObserver {

}

b: notifyObservers 方法

這個方法原本只接受一個參數,就是notification:INotification,改動後則接受3個參數

/**
 * @name: 命令名字
 * @args: 參數列表,可缺省,或爲任意類型的數據
 * @cancelable: 事件是否允許取消,默認爲false
 */
notifyObservers(name: string, args?: any, cancelable: boolean = false): void {

}

值得一說的是 cancelable,改動後的命令是允許被取消的,例子如下

puremvc.Facade.getInstance().registerObserver("TestMessage", handlerA, null, false, 1);
puremvc.Facade.getInstance().registerObserver("TestMessage", handlerB, null, false, 2);

function handlerA(x:number) {
    console.log("handler a is executed");
}

function handlerB(x:number) {
    console.log("handler b is executed");

    // 中斷消息
    puremvc.Facade.getInstance().notifyCancel();
}

puremvc.Facade.getInstance().sendNotification("TestMessage", 5, true);

// 在這個例子中,只會打印如下消息

"handler b is executed"

// 原因之一是 observer 在被註冊時,handlerB 的優先級比 handlerA 高,所以 handlerB 先執行
// 原因之二是 "TestMessage" 在被派發時,指定的 cancelable 是 true,而 handlerB 中調用了notifyCancel 方法,所以消息在 handlerB 響應結束之後,被中斷傳遞了

這裏有個需要注意的是,我公開了 registerObserver 方法,但並沒有去給命令作確保執行的處理,所以,如果你即註冊了命令,又在 mediator中註冊了回調,那麼,你在 mediator 中使用 notifyCancel 的時候,需要特別注意這個問題,如果你希望命令使終都能被執行,則建議你先註冊命令,再註冊中介者

c: removeObserver 方法

參考 flash as3 中的 EventDispatcher ,很容易就能想到,removeObserver 變成了下面這個樣子了

removeObserver(name: string, method: Function, caller: Object): void {
}

d: $isCanceled:boolean 屬性

這個屬性是用來實現 notifyCancel 的邏輯的

e: $observers:Array<boolean | IObserver > 屬性

這個數組,在原版puremvc中,是 Array<IObserver>,爲什麼我要加上 boolean 呢?因爲原版的puremvc中,每次執行 notifyObserver 時,都會對 objservers 進行復制,但如果消息在派發過程中,沒有任何方法被註冊或註銷的話,observers 屬性就不會發生變化,這時這個複製就是多餘的

所以,我對它這部分作了性能優化,我在 Array[0] 中保存了一個 boolean 默認爲 false ,在 notifyObserver 被調用時,我會將這個 boolean 賦值爲 true ,執行完畢後,再重置爲 false ,當 registerObserver 或 removeObserver 被調用時,我先讀取 Array[0] 的值,若爲 true ,則說明這個消息正在執行,此時 notifiyObserver 和註冊註銷會形成干擾,這時候就對 $observers 數組進行復制,複製完再將新的數組中的 Array[0] 重置爲 false ,因爲後面的註銷和註冊行爲操作的是新數組,己不會再對本次的 notifyObserver 產生干擾了。

這步優化可以省去很多數組複製的操作

5 IFacade

由於上述的改動,外觀類中的接口當然也需要一起被改變,不過我在 Facade 中還開放了 registerObserver 和 removeObserver 方法,開放這兩個方法的原因,命令派發雖然可以從任何地方發起,但命令的響應除了 ICommand 之外,卻只能存在於 mediator 中,這種設定迫使不得不去定義一些本來沒必要存在的命令或中介者,帶來的影響除了性能的下降之外,還有額外增多的代碼工作量,基於此,有些人選擇放棄了puremvc,還有些人選擇寫一個全局的事件派發器,而我選擇了重構puremvc /手動滑稽

結語:遵重原著版權,puremvc版權歸原作者 Frederic Saunier 所有,若有侵犯,煩告知,另外附上 git 地址,歡迎下載使用

https://github.com/syfolen/laya-puremvc-typescript

PureMVC Standard Framework for TypeScript - Copyright © 2012 Frederic Saunier

PureMVC Framework - Copyright © 2006-2012 Futurescale, Inc.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章