PureMVC(AS3)剖析:實例


PureMVCAS3)剖析:實例

實例

上篇介紹了MVC的思維方式“代碼重用(code reusability)、關注點分離(separation of concernsSoC)”,並介紹了PureMVC框架的設計。本篇從一個實例出發,詳細介紹PureMVC框架中的元素、推薦的項目目錄組織方式、代碼格式等等。

1.  PureMVC模塊划分

上篇中介紹了PureMVC框架設計中存在的角色,這里先回顧一下:經典MVC元設計模式中的三部分由三個單例類管理,分別是Model ViewControllerPureMVC中還有另外一個單例類——FaçadeFaçade提供了與核心層通信的唯一接口。4個單例類構件了PureMVC的骨架

實際上,我們知道一個游戲或項目由多個模塊組成(如登陸/注冊模塊、主場景模塊、關系鏈模塊等等),每個模塊通常都是單獨的ModelViewControllerPureMVC的那4個單例類是滿足不了這樣的需求的,但是它提供了ProxyMediatorCommand來解決這個問題。

clip_image002

1PureMVC項目的模塊划分

ProxyMediatorCommand分別對應MVC中的ModelViewController,也分別有對應的單例管理Model保存所有的Proxy引用、View保存所有的Mediator引用、Controller保存所有的Command映射:

l  Proxy: Proxy 負責操作數據模型,與遠程服務通信存取數據;

l  Mediator: Mediator操作具體的視圖組件UI,包括:添加事件監聽器,發送或接收 Notification ,直接改變視圖組件的狀態;

l  Command: Command 可以獲取 Proxy 對象並與之交互,發送 Notification,執行其他的 Command

2.  推薦PureMVC初始化流程

PureMVC框架的入口是繼承Façade的子類:ApplicationFacade(這個隨你喜歡,startUp()方法啟動初始化框架。

ApplicationFacade類:

       public class ApplicationFacade extends Facade implements IFacade

         {

                   private static const STARTUP:String = "startup";

                  

                   public static function getInstance():ApplicationFacade

                   {

                            if (instance == null)

                                     instance = new ApplicationFacade();

                            return instance as ApplicationFacade;

                   }

                  

                   override protected function initializeController():void

                   {

                            super.initializeController();

                            registerCommand(STARTUP, StartupCommand);

                   }

                  

                   public function startUp(rootView:DisplayObjectContainer):void

                   {

                            sendNotification(STARTUP, rootView);

                            removeCommand(STARTUP); //PureMVC初始化完成,注銷STARUP命令

                   }

這個類有幾點需要說明的:

n  它是PureMVC應用程序的入口。ApplicationFacade 類對象負責初始化Controller(控制器),建立CommandNotification 名之間的映射。

n  ApplicationFacade 類僅定義Notification(通知)常量:STARTUP private),標識應用程序啟動,其它Notification(通知)常量抽離到ApplicationConstants中定義,這樣更簡潔、清晰。

n  為了使ApplicationFacade結構更清晰,簡潔。將注冊CommandProxyView&Mediator的工作抽離到BootstrapCommandsBootstrapModelsBootstrapViewMediators去做。

l  BootstrapCommands:初始化應用程序事件與Command之間的映射關系;

l  BootstrapModelsModel 初始化,初始化應用程序啟動過程中需要用到的Proxy,並注冊;

l  BootstrapViewMediatorsView 初始化,唯一創建並注冊ApplicationMediator,它包含其他所有View Component並在啟動時創建它們。

調用startUp()啟動應用程序,發送STARTUP命令;然后觸發StartupCommand,它包含三個子command執行(這里借鑒Robotlegs的思想,將CommandModelViewMediator初始化工作分離,使得程序結構更清晰。)

clip_image004

2 StartupCommand包含3個子命令BootstrapCommandsBootstrapModelsBootstrapViewMediators

簡而言之,框架初始化流程可以表示如下:

clip_image006

3 PureMVC應用程序框架初始化流程

 

3.  推薦PureMVC結構

clip_image007

4:推薦目錄結構(圖為連連看例子的目錄結構)

推薦目錄結構為,按MVC分為3個目錄:modelviewcontrollerModel下面有定義vo的子目錄,view下面有定義UI界面的子目錄ui等,controller下面有定義初始化的子命令目錄boostraps。實際項目中的基本上每個功能模塊,在三個目錄下對應的類。

4.  PureMVC模塊間通信

當一個模塊需要與其它模塊交互時,可以通過發送/接收Notification或者通過façadefacade.retrieveMediatorfacade.retrieveProxy檢索到指定模塊,然后調用相應接口。

Flash事件和PureMVC通知的主要差異是:事件遵循“責任鏈”模式,在顯示層級中“冒泡”直到有父組件處理它;而通知遵循“發布/訂閱”模式。使用通知進行通信,PureMVC各模塊之間不需要建立父子關系。

通知並不是事件的替代物。一般情況下,Mediator給其視圖組件添加事件偵聽器,按常用方式處理,然后給目標Command廣播Notice,或與其他Mediator通信。Proxy通過廣播Notice,與Command實例和Mediator通信。

推薦使用Notification機制,但是全部使用Notification這種強松耦合模式:①強松耦合加重通信次數;②帶反饋數據的通信加重通信負擔。適當使用直接通信方式。

clip_image009

5PureMVC之間的通信

5.  PureMVC實例:連連看游戲

首先聲明“連連看”游戲並非很適合使用PureMVC框架,因為它本身並不復雜,而且沒有需要復用的邏輯代碼等。這里通過這樣一個不太復雜的小游戲,介紹PureMVC

需求

《連連看》是一款操作非常簡單的小游戲,它的玩法就是用直線將兩個相同的圖標消掉,分為十關,難度遞進。

【概要】玩家可以將 2 個相同圖案的對子連接起來,連接線不多於 3 根直線,就可以成功將對子消除。

【操作】第一次使用鼠標點擊棋盤中的棋子,該棋子此時為“被選中”,以特殊方式顯示;再次以鼠標點擊其他棋子,若該棋子與被選中的棋子圖案相同,且把第一個棋子到第二個棋子連起來,中間的直線不超過 3 根,則消掉這一對棋子,否則第一顆棋子恢復成未被選中狀態,而第二顆棋子變成被選中狀態

設計

整個游戲分為2個模塊:關卡選擇模塊、關卡具體模塊。關卡選擇模塊,類似一個菜單呈現10個關卡供玩家選擇;關卡具體模塊,呈現特定關卡的詳細信息,如需要消除的棋子、總時間、重排棋子按鈕、獲取提示按鈕、暫停按鈕等等。

關卡數據的幾點說明:

n  關卡數據可以通過關卡編輯器生成配置文件,然后讀取配置文件即可。這樣關卡更可控,且可以擺放成各種形狀(心形、回形等等)的數據。

n  如果做成網絡版的,關卡數據也可以通過服務器端返回。

本實例為了簡單,關卡數據根據等級隨機生成10種圖片,數量固定為40個。

關卡難度遞進設定:

n  第一,從時間遞減來增加難度,30 * (11 - level)

n  第二,從可以重新擺放棋子、獲取提示次數來體現。

n  如果作為一個商用連連看,還可以通過棋子的種類數量、定時隨機重排增加難度。

判斷連通算法,可以使用多種:分支界定判斷廣度優先搜索法四個方向的A*算法等等,例子不考慮性能簡單的使用了分支界定判斷,而且這里也不會深入介紹,畢竟我的目的不是介紹連連看算法,有興趣的自行了解。

實現

詳細代碼我已放到GitHubhttps://github.com/saylorzhu/linkupGame,自行前往查看。我們做技術的,看代碼就可以了解如何使用PureMVC了,看到這里更推薦你去看代碼。下面我只介紹代碼的幾個PureMVC實現的關鍵地方。

注意點1PureMVC框架初始化流程,及代碼組織

這也是我想推送給大家的,框架初始化流程可參見【2. 推薦PureMVC初始化流程】,代碼組織方法可參見【3. 推薦PureMVC結構】。

注意點2:模塊划分與通信

游戲分為2個模塊:關卡選擇模塊、關卡具體模塊。

n  關卡選擇模塊包括:

framework.view.componets.ChooseLevCanvasMediator

framework.view.componets.ui.ChooseLevCanvas

framework.controller.commands.ChooseLevCommand

該模塊不用存儲數據,沒有Proxy

ChooseLevCanvasChooseLevCanvasMediator組成ViewChooseLevCanvas負責具體UI展示和操作響應,ChooseLevCanvasMediator負責將來自用戶操作的事件轉發給PureMVC框架並將來自框架的事件轉發給ChooseLevCanvas

ChooseLevCommand由玩家選擇具體關卡事件ApplicationConstants.SELECT_LEVEL觸發,該Command調用LevelProxy的接口生成關卡數據,然后通知到“關卡具體模塊”開始游戲。

n  關卡具體模塊包括:

framework.view.componets.LevelCanvasMediator

framework.view.componets.ui.LevelCanvas

framework.model.LevelProxy

framework.controller.commands.ReSortCommand

LevelProxy 屬於Model,生成、存儲、重排具體關卡的數據,負責數據相關操作。

LevelCanvasLevelCanvasMediator組成ViewLevelCanvas負責具體UI展示和響應玩家操作,LevelCanvasMediator負責將來自用戶操作的事件轉發給PureMVC框架並將來自框架的事件轉發給LevelCanvas

ReSortCommand由玩家點擊重排按鈕觸發,該Command調用LevelProxy的接口重排關卡中的棋子。

這里涉及到的通信方式有,通知flash內置事件(xxxCanvaxxxCanvasMediator

Command需要偵聽通知,需要在framework.controller.boostraps.BootstrapCommands中使用registerCommand注冊;

xxxCanvasMediator需要偵聽通知,需要在對應Mediator中使用listNotificationInterests注冊,並重寫handleNotification處理。

模塊間通信可參見【4.PureMVC模塊見通信】。

注意點3CommandProxyMediator

Command管理應用程序的 Business Logic(業務邏輯),要協調Model 與視圖狀態

Model 通過使用 Proxy 來保證數據的完整性、一致性 Proxy 集中程序的Domain Logic(域邏輯),並對外公布操作數據對象的API。它封裝了所有對數據模型的操作,不管數據是客戶端還是服務器端的。

Mediator Proxy 可以提供一些操作接口讓Command 調用來管理View Component Data Object ,同時對 Command隱藏具體操作的細節。

注意點4:一般一個MediatorhandleNotification方法)處理的Notification應該在45個之內。

還要注意的是,Mediator的職責應該要細分。如果處理的Notification很多,則意味着Mediator需要被拆分,在拆分后的子模塊的Mediator里處理要比全部放在一起更好。

注意點5:應該避免MediatorProxy 直接交互。

例子項目中遵從了這個規則,但實際上項目Mediator中不可避免需要獲取Proxy數據,如果每次都通過一個Notification去獲取數據,然后返回數據給Mediator這樣無形中增加了通信次數、帶反饋數據的通信加重通信負擔。所以可以適當是的在Mediatorfacade.retrieveProxy獲取Proxy然后拿到數據,而且從proxy直接拿數據,可以保證拿到最新數據。 

參考資料

[1]     PureMVC官方網站:www.puremvc.org

[2]     Wikipediahttp://zh.wikipedia.org/zh-cn/MVC

[3]     PureMVC_Implementation_Idioms_and_Best_Practices_cn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM