Laya 中使用 PureMVC

參考
PureMVC(AS3)剖析
我的FLASH情結2010 - 淺談網(wǎng)頁游戲與創(chuàng)業(yè)
菜鳥學PureMVC記
PureMVC(JS版)源碼解析:總結
Flash務實主義(四)——Flash中的MVC

一槐瑞、痛點

參考
android mvc mvp mvvm
PureMVC--一款多平臺MVC框架

1.傳統(tǒng)MVC的痛點

  • Controller:控制器邑滨,包含了項目的業(yè)務邏輯诊霹。但是也是被大家吐槽最多的一個奇瘦,原因就是很多人瑟捣,或者說大多數(shù)人陕悬,習慣于什么都往Controller里寫习柠,最后一個Controller超過1000行代碼是司空見慣的事疏魏。所以關于傳統(tǒng)MVC的第一個痛點就是蛉艾,Controller過于臃腫钳踊。

  • Model:模型衷敌,包含了項目的數(shù)據(jù)模型。MVC定義之初拓瞪,Model是核心缴罗,旨在使得同一個Model可以被復用到多個項目或者被復用到同一個項目的不同模塊之中。但是在實際項目中祭埂,Model還承載著純Model層內部的運算的工作面氓,但是運算部分會項目的不同而有所區(qū)別,因此與項目的適配反而成為了Model可復用的枷鎖蛆橡。所以關于傳統(tǒng)MVC的第二個痛點就是舌界,Model變得不可復用。

  • View:視圖泰演,包含了項目所有的UI組件呻拌。視圖本身沒有什么好被大家詬病的,但是由于MVC中對于View和Controller界限的模糊界定造成了使用者在寫代碼的時候會覺得這部分代碼放在View或者Controller里都可以的情況睦焕。例如事件的處理藐握,組件的組合等。所以關于傳統(tǒng)MVC的第三個痛點就是垃喊,View概念的模糊猾普。

2.PureMVC到底如何解決了傳統(tǒng)MVC的三個痛點?

  • Controller將操作邏輯細化為Command
    根據(jù)PureMVC的最佳實踐本谜,Controller實體不需要單獨實現(xiàn)初家,且Controller內部將每一個操作分割為一個個Command,這從根本上解決了Controller越來越臃腫的問題耕突,強制用戶將Controller里每一個操作細粒度化笤成,使得代碼可讀性更強,維護性更高眷茁。

  • Proxy負責域邏輯炕泳,DataObject負責數(shù)據(jù)模型
    PureMVC中,與域相關的邏輯和接口由Proxy來負責上祈,后續(xù)的添加和修改接口只在Proxy中完成培遵。而DataObject是完全對業(yè)務進行數(shù)據(jù)建模而產(chǎn)生的數(shù)據(jù)模型,與業(yè)務沒有絲毫的關系登刺,因此也保證了高可移植性籽腕。

  • ViewComponent只關注UI,其余的交給Mediator
    PureMVC規(guī)定了ViewComponent只負責UI的繪制纸俭,而其他事情皇耗,包括事件的綁定統(tǒng)統(tǒng)交給Mediator來做。這也就避免了ViewComponent內部代碼定義模糊揍很,更不會和Controller的代碼進行混淆郎楼。

二万伤、使用細節(jié)
PureMVC 工作流程圖

1.Command需要偵聽通知,使用registerCommand注冊事件偵聽
根據(jù)《pureMVC最佳實踐》中的建議呜袁,我的做法是這樣的敌买,盡量使用Command,讓Command成為Mediator與Proxy之間通訊的唯一橋梁阶界,Mediator和Proxy中發(fā)出的Notification虹钮,接收者一定是某個Command,然后再由Command處理并將結果轉發(fā)給真正的消息接收者膘融,Command就算僅僅起一個轉發(fā)作用芙粱,僅僅有不到10行代碼,也要創(chuàng)建一個Command類托启。這樣不僅使你的架構更加清晰宅倒,而且也更符合MVC思想,Command類的大量存在還使你架構的業(yè)務邏輯具有了更好的封裝性和擴展性屯耸,可謂是一箭三雕拐迁,何樂而不為?唯一的負面影響可能是你需要創(chuàng)建和維護更多的Command類文件疗绣,但相對于優(yōu)勢而言线召,這點影響不算啥。

參考PureMVC(JS版)源碼解析(六):MacroCommand類
在實際開發(fā)過程中多矮,我們需要寫一些復雜的邏輯處理單元(Command類)缓淹,這寫邏輯處理類要么繼承SimpleCommand,要么繼承MacroCommand類塔逃,哪什么時候繼承MacroCommand類讯壶,什么時候繼承SimpleCommand類,需要看我們邏輯的復雜度湾盗,如果一個邏輯單元可以拆分為多個子邏輯單元伏蚊,那我們可以繼承MacroCommand類,如果一個邏輯單元就可以處理格粪,那我們只需要繼承SimpleCommand類躏吊。繼承MacroCommand類的子類需要重寫initializeMacroCommand方法,不需要重寫execute方法帐萎,繼承SimpleCommand類的子類需要重寫execute方法比伏。

參考PureMVC(JS版)源碼解析(十):Controller類
executeCommand()方法告訴我們, Command對象(SimpleCommand或者MacroCommand)是無狀態(tài)的疆导;只有在需要的時候(Controller收到相應的Notification)才會被創(chuàng)建赁项,并且被執(zhí)行(調用execute方法)。

2.xxxCanvasMediator需要偵聽通知,需要在對應Mediator中使用listNotificationInterests注冊肤舞,并重寫handleNotification處理紫新。

3.Proxy 集中程序的Domain Logic(域邏輯),并對外公布操作數(shù)據(jù)對象的API李剖。它封裝了所有對數(shù)據(jù)模型的操作,不管數(shù)據(jù)是客戶端還是服務器端的囤耳。

模型部分在實際開發(fā)中除了存儲數(shù)據(jù)篙顺,還有其他作用么?是的充择,其實它的實際職責非常多德玫。它要給Command和Mediator提供接口,響應用戶操作椎麦,進行數(shù)據(jù)操作或者請求遠程數(shù)據(jù)服務宰僧,進行數(shù)據(jù)的序列化和反序列化,得到異步數(shù)據(jù)后可能還要檢查數(shù)據(jù)合法化观挎。但不管怎么樣琴儿,它始終是在和數(shù)據(jù)打交道,同時也應該是你的主程序中唯一可以直接和數(shù)據(jù)打交道的管道嘁捷,別的部分要想和數(shù)據(jù)有接觸造成,首先要問問它同意不同意。模型處理完數(shù)據(jù)會以Notification的消息方式通知Command或者Mediator雄嚣。但絕對不能在Proxy中直接調用Mediator晒屎,這是為了保證數(shù)據(jù)層的獨立性、可移植性和重用性缓升,也簡化了你的架構思想鼓鲁。
參考PureMVC(JS版)源碼解析(八):Proxy類
另外,和Mediator類一樣港谊,Proxy類也要在facade中注冊骇吭,它也有onRegister()/onRemove()方法,這里就不在陳述封锉,具體用法和Mediator類的onRegister()/onRemove()用法一樣绵跷。
總結一下:Proxy類的結構很簡單,我們只需要記住兩點成福,一是它有一個data屬性碾局,可以用來存儲它所管理的data model,二是它可以發(fā)送消息,不能接受消息奴艾。

4.應該避免Mediator與Proxy 直接交互净当。例子項目中遵從了這個規(guī)則,但實際上項目Mediator中不可避免需要獲取Proxy數(shù)據(jù),如果每次都通過一個Notification去獲取數(shù)據(jù)像啼,然后返回數(shù)據(jù)給Mediator俘闯,這樣無形中增加了通信次數(shù)、帶反饋數(shù)據(jù)的通信加重通信負擔忽冻。所以可以適當是的在Mediator中facade.retrieveProxy獲取Proxy然后拿到數(shù)據(jù)真朗,而且從proxy直接拿數(shù)據(jù),可以保證拿到最新數(shù)據(jù)僧诚。

5.永遠不要把 Event 的名稱定義在 Fa?ade 類里遮婶。應該把 Event 名稱常量定
義在那些發(fā)送事件的地方,或者就定義在 Event 類里湖笨。

6.參考Pure MVC使用的幾點心得(模式和使用誤區(qū))
注冊mediator/command/proxy的時機不合適和沒有及時注銷旗扑。當初所有command/proxy的注冊我都是放在應用初始化的時候做,就是無論用戶是否真的用到這些功能慈省,都已經(jīng)注冊(占用客戶端的內存)了臀防。另外比較嚴重的一點是,根本沒有注銷边败!那樣會怎樣袱衷,客戶端的內存會一直占用,直到退出瀏覽器放闺,后果可想而知祟昭。正確的做法當然是有需求才去滿足需求啦。當用戶點擊某個button怖侦,menu篡悟,下拉菜單時,才去注冊mediator/command/proxy匾寝,用完之后及時注銷搬葬。當然,如果有些功能模塊是經(jīng)常用到的艳悔,可以常駐內存急凰,這樣會提高一點效率。

7.參考Flash務實主義(四)——Flash中的MVC
是Mediator知道View的一切猜年,View完全不知道Mediator抡锈,而不是相反
對于使用pureMVC的同僚們,我真是不明白你們到底是怎么把這個反過來理解成“View知道Mediator的一切乔外,而Mediator完全不知道View”的床三,因為官方實例上寫的很明白。估計是把mediator當成通信專用的模塊類了吧杨幼。但是如果你放棄了mediator分離View代碼的特性撇簿,只是用來通信的話聂渊,至少要保留原來的通信功能,就是讓Mediator依然可以直接訪問View四瘫。否則既然Mediator是用來通信的汉嗽,它卻不能操作View,結果還得設法和View再通信一次……

8.參考PureMVC的十個小提示 原作者杜增強
在某些情況下你不再使用一個Mediator和它的View Components. 你應該用facade.removeMediator(MyMediator.NAME)去掉這個Mediator同時用 destroy()來去掉包含所有l(wèi)isteners,timer,references的ViewComponent.以便更好的進行 垃圾回收.

9.參考PureMVC閑談(五):pureMVC的QA

Q:Mediator可以handleNotification找蜜,Command也可以和notification關聯(lián)饼暑,到底怎么來決定用誰處理呢?
A:還是回到純·摸魚吃锹杈,什么時候要軍隊自己去接通知撵孤,什么時候需要將軍來接通知執(zhí)行呢?通常情況下竭望,“內部的小事”就由軍隊自己處理,比如騎兵一隊的馬餓了裕菠,發(fā)出一個通知“勞資餓了”咬清,那么騎兵一隊自己接這個通知然后去喂馬就好了,這樣做的好處就是保證了主要邏輯的簡潔奴潘,不用勞煩將軍再去操心這些小事旧烧。那么“關系到集體的大事”就得由將軍去處理了,比如通知需要哪幾只部隊連夜突襲画髓,發(fā)一個通知讓將軍來調度更加安全保險掘剪,這樣做另外的好處就是重用性好 ,否則每個軍隊都得去接這個通知奈虾,然后看看是不是通知了自己夺谁,萬一有些軍隊忘了接這個通知,打仗輸了可就慘咯肉微!(簡單的邏輯交給Mediator處理)
Q:在registerMediator的時候匾鸥,viewComponent怎么寫?
A:寫它要管理的視圖對象
Q:我怎么看見有人寫的 viewComponent是main碉纳,而不是管理的視圖對象勿负?
A:說明它要在內部創(chuàng)建視圖對象,然后添加到main上面劳曹,所以傳入了main奴愉。這樣做就完全把那個視圖對象 給保護了,還是那句話铁孵,這是很靈活的锭硼。
心得:
1.一個Command也可以關心多個通知,然后用一個Switch來判斷處理库菲。也可以給通知加上Type屬性账忘,在處理時進行分別處理。
2.多思考結構,積極重構鳖擒,這會讓你的程序后期維護和改進非常容易
3.建立一個AppMediator來管理整個main視圖溉浙,這樣就能提供他的viewComponent來獲取main
4.建一個PublicNotification類來提供常用的通知名稱
5.用onRemove()函數(shù),及時清除對象釋放內存
6.靈活處理蒋荚,教科書只是參考戳稽,按你自己的方法也許更快更方便。
7.不要為了MVC而MVC期升,如果一個東西要徹底解耦是不太現(xiàn)實的惊奇,一些不會擴展的小東西完全可以怎么快怎么來。

10.參考總結PureMVC中Mediator,Command,Proxy的職責

image.png

11.參考PureMVC(AS3)剖析:吐槽
為了模塊間解耦播赁,Notification發(fā)布者應該不關心誰對這個消息感興趣(誰來處理)颂郎,感興趣者自行注冊(Mediator通過listNotificationInterests注冊、Command通過facade.registerCommand()注冊)容为。

無法知道Notification的源頭乓序。然而這點可以通過在消息體body中,增加字段標識坎背,如:

sendNotification(ApplicationConstants.UPDATE_LEVEL_DATA, 
{ "noticeSource": this, "levelData": m_levelData } );

noticeSource標識消息來源替劈,如果您還想要知道消息傳遞層次,可以用數(shù)組表示得滤,順序插入傳遞者陨献。

三、Egret中使用PureMVC

參考
使用egret開發(fā)2048
關于使用pureMvc開發(fā)2048懂更,出現(xiàn) Cannot find name 'puremvc'.問題
如何在egret中使用pureMVC

我們只需要他提供的一個js文件和一個.d.ts文件眨业。 然后復制到對應位置。改一下index.html和release.html如果要打包成native還要改一下native_loader.js膜蛔,具體怎么改你看GitHub上對應的文件 坛猪。 這里還有一點沒有說清楚的是 pureMVC的作者給的那個js不要用,他多了一點define的東西皂股,我把他去掉了就可以在ts項目里面用了墅茉。

引用了第三方類庫之后要先 build -e 一次的。

四呜呐、Laya 中使用 PureMVC

1.同Egret,把define那部分注釋掉就斤。
2.注意,如果下載了multicore版本蘑辑,寫法有一點點區(qū)別洋机。大多數(shù)語言都支持兩個版本的框架,一個是標準版洋魂,不支持多模塊绷旗,另一個是multicore版喜鼓,支持多模塊。參考pureMVC衔肢,多核和單核的區(qū)別,多核可以有多個Facade的實例庄岖,單核只有一個

class AppFacade extends puremvc.Facade {
    constructor() {
        super();
    }
    public static readonly STARTUP: string = "startup";
    public static readonly MOUSE_CLICK: string = "MOUSE_CLICK";

    public static getInstance(): AppFacade {
        if (this.instance == null) this.instance = new AppFacade();
        return <AppFacade>(this.instance);
    }

    public initializeController(): void {
        super.initializeController();
        this.registerCommand(AppFacade.STARTUP, StartupCommand);
        this.registerCommand(AppFacade.MOUSE_CLICK, MouseClickCommand);
    }

    /**
     * 啟動PureMVC,在應用程序中調用此方法角骤,并傳遞應用程序本身的引用
     * @param   rootView    -   PureMVC應用程序的根視圖root隅忿,包含其它所有的View Componet
     */
    public startUp(rootView: any): void {
        this.sendNotification(AppFacade.STARTUP, rootView);
        this.removeCommand(AppFacade.STARTUP); //PureMVC初始化完成,注銷STARUP命令
    }
}

class StartupCommand extends puremvc.SimpleCommand {

    public constructor() {
        super();
    }
    public execute(notification: puremvc.INotification): void {
        this.facade.registerProxy(new MouseClickProxy());
        var stage: Laya.Stage = notification.getBody() as Laya.Stage;
        this.facade.registerMediator(new StageMediator(stage));
    }
}

class MouseClickProxy extends puremvc.Proxy implements puremvc.IProxy {
    public static NAME: string = "MouseClickProxy";
    public constructor() {
        super(MouseClickProxy.NAME);
    }
}

class StageMediator extends puremvc.Mediator implements puremvc.IMediator{
        public static NAME:string = "StageMediator";
        
        public constructor(viewComponent:Laya.Stage){
            super(StageMediator.NAME, viewComponent);
            //注冊監(jiān)聽邦尊,從而獲取UI上的事件
            viewComponent.on(Laya.Event.CLICK, this,this.mouseClick);
        }

        private mouseClick(event:Laya.Event):void
        {
            this.sendNotification(AppFacade.MOUSE_CLICK, event.stageX);
        }
}

class MouseClickCommand extends puremvc.SimpleCommand {

    public constructor() {
        super();
    }

    public execute(notification: puremvc.INotification): void {
        var stageX: number = <number>notification.getBody();
        console.log("click pos:",stageX);
    }
}

// 程序入口
class GameMain{
    constructor()
    {
        Laya.init(600,400);
        AppFacade.getInstance().startUp(Laya.stage);
    }
}
new GameMain();

multicore版本部分代碼

    public static BG_NAME:string = "BGH5Video";
    public static readonly STARTUP: string = "startup";
    public static readonly MOUSE_CLICK: string = "MOUSE_CLICK";

    // public static getInstance(): AppFacade {
    //     if (this.instance == null) this.instance = new AppFacade();
    //     return <AppFacade>(this.instance);
    // }

        public static getInstance():AppFacade {
            if( !AppFacade.instanceMap[ AppFacade.BG_NAME ] )
                AppFacade.instanceMap[ AppFacade.BG_NAME ] = new AppFacade( );
            return AppFacade.instanceMap[ AppFacade.BG_NAME ];
        }

class StartupCommand extends puremvc.SimpleCommand {

    public constructor() {
        super();
    }
    public execute(notification: puremvc.INotification): void {
        this.facade().registerProxy(new MouseClickProxy());
        var stage: Laya.Stage = notification.getBody() as Laya.Stage;
        this.facade().registerMediator(new StageMediator(stage));
    }
}
五背桐、puremvc 場景切換

使用 PureMVC 構建游戲項目 Part IV 之場景管理
注意,如果關閉某個窗口蝉揍,只是把它從舞臺移除链峭,但是沒有removeMediator時。此mediator通過listNotificationInterests偵聽的事件仍然會執(zhí)行
end

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末又沾,一起剝皮案震驚了整個濱河市熏版,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捍掺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件再膳,死亡現(xiàn)場離奇詭異挺勿,居然都是意外死亡,警方通過查閱死者的電腦和手機喂柒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門不瓶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人灾杰,你說我怎么就攤上這事蚊丐。” “怎么了艳吠?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵麦备,是天一觀的道長。 經(jīng)常有香客問我昭娩,道長凛篙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任栏渺,我火速辦了婚禮呛梆,結果婚禮上,老公的妹妹穿的比我還像新娘磕诊。我一直安慰自己填物,他們只是感情好纹腌,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滞磺,像睡著了一般升薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雁刷,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天覆劈,我揣著相機與錄音,去河邊找鬼沛励。 笑死责语,一個胖子當著我的面吹牛,可吹牛的內容都是我干的目派。 我是一名探鬼主播坤候,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼企蹭!你這毒婦竟也來了白筹?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤谅摄,失蹤者是張志新(化名)和其女友劉穎徒河,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體送漠,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡顽照,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了闽寡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片代兵。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爷狈,靈堂內的尸體忽然破棺而出植影,到底是詐尸還是另有隱情,我是刑警寧澤涎永,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布思币,位于F島的核電站,受9級特大地震影響土辩,放射性物質發(fā)生泄漏支救。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一拷淘、第九天 我趴在偏房一處隱蔽的房頂上張望各墨。 院中可真熱鬧,春花似錦启涯、人聲如沸贬堵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黎做。三九已至叉跛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蒸殿,已是汗流浹背筷厘。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宏所,地道東北人酥艳。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像爬骤,于是被迫代替她去往敵國和親充石。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內容