一點點感想:寫程序都快十年里,幾乎沒有用過什么大型的框架,實為懺愧,其實對於設計模式還是耳熟能詳,mvc,facade,factory模式等等,其實在自己代碼中都會用到,不過都是自己寫的,沒有框架那么復雜,但可定是符合實際設計需要的。MVC設計模式主要是解決了代碼耦合性的問題,當為此給程序帶來的理解問題就會多一些了,畢竟順序思維還是好理解一點。由於項目中原來自己寫的MVC框架在代碼不斷的增加,已經變得難以理清頭緒了,尤其是在Flex的時間和回調中交織時,變得由為困難。遂決定用一個開源的MVC框架來理清我的問題。由於需要設計的是一個需要高效運行的設計器,而且設計器中的元素會需要不斷的加載和刪除,使用Robotlegs框架就有點困難,他是通過監聽舞台元素添加事件實現消息注入的,對設計器而言,需要不斷地加載和刪除就會產生效率問題,最后回歸到了puremvc框架上了。其實,知道MVC模式和可以使用,並用好MVC模式寫代碼,還是需要一段時間的。MVC模式如果簡單的理解就是通過將模型,視圖用控制器分開來實現模型和視圖的松耦合,那么如何實現的呢?有應該以什么樣的方式來寫自己的代碼,並真正實現MVC松耦合的目的呢?本人通過自己的理解,來學習puremvc框架,以解決上述問題,本文的思路也是通過帶着問題的方式來逐步學習的。
初始問題:
- MVC的三部分在puremvc中是如何對應的?
- puremvc是如何實現松耦合的?
- puremvc是如何來構建代碼的?
本文通過puremvc的官方教程《puremvc實現術語闡述及最佳實踐》一書和Login的例程來學習的,筆者也是剛學習,希望可以一同探討。閱讀本文需要Flex的基本知識和MVC基本概念,還有一顆對知識無線追求的心。
首先,我們通過書本上的知識來梳理一下MVC如何在puremvc中的體現。

如上圖所示,MVC三部分通過消息總線(MVC內部必須實現),來實現模型與視圖的分離,並通過控制器實現了業務邏輯的處理。對於常見的場景就是加載數據,用戶修改數據,再保存數據,在MVC模式下,加載數據有用戶在View上發起,通過Command交給Controller,Controller調用Model取得數據,當數據取得后,發送通知消息到消息總線,相應的View會接受消息,並完成數據的顯示,用戶在View上修改后,通過Controller再將數據寫回Model。如此就是一個MVC模式下普通的工作場景,這樣的好處是:View和Model相互的關聯性很少,View需要依據自己的需求呈現數據即可,Model實現自己的數據加載和存儲,如果需要修改View的代碼,Model部分就不需要修改,保持了代碼的局部穩定性。另外,消息總線很好地實現了一對多的消息傳遞,可以實現多個視圖同時更新的需求。以上是MVC的簡單模式,在puremvc中,就具體化多了。

消息總線使用Notification對象傳遞,Controller通過Command進行處理,機制與前面講述的一樣,只是View有Mediator(中介),Model由Proxy(代理)處理具體的邏輯。這里就回答了puremvc是如何實現MVC模型的問題了。
下面通過Login的具體例子來分析puremvc如何使用,以及他實現松耦合的。
首先,如果需要下載代碼請下載Github上的代碼,不過筆者發現Flash Builder4.6無法編譯,所以自己新建了一個工程PureMVCLogin的項目,如果有需要,我可以傳到空間中。
為了理清Login程序的脈絡,我繪制了類圖,並表示了程序的初始化路徑(藍色關聯線條)。

再來一張時序圖,講述了Login各模塊的工作順序(可放大查看)。

Login的前期初始化順序就是從創建Facade開始的,Facade為單例模式,通過ApplicationFacade.getInstance()建立Facade實例,完成實例化后偶,puremvc已經可以開始內核就初始化完成了。下一步就是建立應用系統的相關工作作了。ApplicationFacade代碼如下:
public class ApplicationFacade extends Facade
{
/**
* Notification name constants
*
*/
public static const APP_STARTUP: String = "appStartup";
public static const LOGIN: String = "login";
/**
* Singleton ApplicationFacade Factory Method
*/
public static function getInstance(): ApplicationFacade
{
if (instance == null) instance = new ApplicationFacade();
return instance as ApplicationFacade;
}
/**
* Register Commands with the Controller
*/
override protected function initializeController() : void
{
super.initializeController();
//
// register commands
registerCommand( APP_STARTUP, ApplicationStartupCommand );
registerCommand( LOGIN, LoginCommand );
}
public function startup(app: PureMVCLogin):void
{
this.sendNotification( ApplicationFacade.APP_STARTUP, app );
}
}
在Application中開始取得facade的實例后,在Application的createComplete事件中執行facade的startup函數,該函數發送APP_STARTUP通知,並帶有app對象,app對象保護view相關的應用(在初始化后面的Mediator時需要),由於在facade在initializeController中注冊了APP_STARTUP和LOGIN命令,在startup調用開始是,puremvc內核已經可以工作了,接下來的事情就是初始化與具體應用相關的任務了。
ApplicationStartupCommand為宏命令,即就有多個命令集合的對象,在Login中,他執行了ModelPrepCommand和ViewPrepCommand兩個命令,需要注意的是再宏命令中的命令是按順序執行的,即先初始化模型,在初始化視圖,並且,視圖初始化的時候,會通過facade取得相應的Proxy(代理)引用,因此是有順序要求的。模型的初始化任務就是建立並注冊相應的Proxy到框架中,視圖的初始化任務就是建立總的Mediator(中介)ApplicationMediator,並在其中建立並注冊Mediator到框架中。這樣,Login系統的mvc模式就初始化完了。
需要說明的就是Mediator與View之間是通過Flex的Event事件來傳遞信息的,具體編程模式請仔細看前面的官方教程。本例中LoginPanelMediator注冊了LoginPanel的登陸按鈕事件,即Mediator接管具體界面的事件,再把事件建立好可以被其他模塊處理的對象,並推送給框架(sendNotification),此時Mediator的任務已經完成,就只需等待登陸成功或失敗的消息了。框架接受到Notification事件后,會找到監聽了該通知的Mediator(一般用於多個中介間通訊)或Command(與代理通訊)。Login例程中,LOGIN時間ApplicationFacade初始化的時候注冊了相應的命令,此時框架就會把命令對象創建,並調用Commande的execute方法,LoginCommand代碼如下:
override public function execute(note: INotification) :void
{
var loginVO: LoginVO = note.getBody() as LoginVO;
var loginProxy: LoginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
loginProxy.getUser(loginVO);
}
在上面的代碼中,通過名稱找到對應的Proxy對象,並執行登陸的業務邏輯,即調用getUser方法。這里可以看出Controller對Proxy的控制是接口關聯的,對於View而言不需要知道登陸如何處理,對Controller而言,他需要知道由哪個命令來處理,命令需要知道如何與具體的代理通訊,實現登陸命令。這里並沒有方法返回,如果命令執行成功或失敗,就Proxy來發送具體的Notification,Proxy也不需要擔心結果由誰來處理,它只需要知道如何取得數據,並發送通知即可,通知總線會找到相應的執行體來執行,如果沒有也不是發送者需要擔心的。
// // populate its data object using the result var loginVO: LoginVO = event.result as LoginVO loginVO.loginDate = new Date(); setData( loginVO ); // // change the view state _appProxy.viewState = ApplicationProxy.LOGGED_IN_STATE; // // notify all interested members sendNotification( LoginProxy.LOGIN_SUCCESS );
在Login程序中,當從服務端成功取得登陸后,LoginProxy會通知ApplicationProxy修改他的viewState,並發送一個登陸成功的通知。該通在LoggedInBoxMediator的類中已經通過listNotificationInterests方法訂閱了改通知,這樣改通知就會由LoggedInBoxMediator處理,並最終體現在其關聯的LoggedInBox界面上,如果登陸失敗,方法與此相同,請仔細看前面時序圖的下半部分。
此時,登陸的功能已經在mvc模式的驅動下合作完成了。那么為什么要這么復雜的處理呢?通過Notification在View(Mediator)和Model(Proxy)之間流動,View和Model就完全解耦了,只要Model保持穩定的接口(通知對象),那么重寫View部分是,是不需要修改Model的代碼的。消息訂閱模式解決了需要思考負載事件傳遞的煩惱。比如在Flex開發中,我們會用到很多的模塊(View),如果各模塊之間需要通訊,那么一般的寫法就是相互添加Listener或通過callback方式來傳遞數據,如果相關模塊數量很多的情況下,這些傳遞數據的方式就麻煩了,各模塊修改自己的方法時還需要考慮其他模塊是否會有問題(筆者在寫設計器的過程中深受其害),如果應用puremvc,我會把各模塊通過Notification建立好通訊機制,從而不用當心其他模塊的實現問題了。比如在A模塊修改了一個對象,此時,B,C模塊就可以通過監聽通知來改變自己的對象,作為A模塊,至需要考慮發出通知,其他的就不需要考慮了,這就是消息總線(Notification)的好處了。
此時第二個問題:puremvc是如何實現松耦合的就好回答了吧,通過Notification機制實現Model和View的松耦合。第三個問題:puremvc是如何來構建代碼的通過順序圖已經很好的說明了。
下面請思考:為什么存在直接注冊Notification訂閱的方法和registerCommand的訂閱方法?
.........
筆者看到Login事件通過Command實現,而LOGIN_SUCCESS和LOGIN_FAILED是通過listNotificationInterests方法訂閱的,為什么呢?仔細閱讀了官方教程后找到了答案?
Notification可以通過上述兩種方式進行訂閱,而一般情況下,需要復雜業務邏輯處理的使用Command處理,還有就是官方說Proxy發送但不接受Notification,即如果需要Proxy接受Notification是不行的,需要通過Command來執行Proxy的方法實現修改Proxy下面的Model數據。各Mediator可以通過直接訂閱Notification的方法實現消息處理。請參閱官方教程中的:Mediator和Proxy之間、Mediator和其他Mediator之間的耦合。
本文就Login的示例代碼深入學習了puremvc在ActionScript中的應用,筆者也是在學習中,希望可以共同學習和交流。
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
