Hello PureMVC !
第一次聽到 PureMVC 的時候大概是兩年前,那時候就對這套 MVC Framework 很感興趣,不過當時對於它的架構是有看沒有懂,就一直拖到了現在。
最近因為工作需要用到的關係,所以又再次學習它,經過閱覽無數前輩分享的文章之後,終於初步了解它的運作模式了,因此分享這邊學習心得筆記。
什麼是 MVC ?
一個設計方法(Design pattern 也稱設計模式)。
想初步了解的朋友可以參考維基百科的說明。
網路也有許多相關文章,大略內容就是將程式開發架構分為 Model、View 和 Controller 三個部份的開發方式,建議可以閱讀深入淺出設計模式這本書。
什麼是 PureMVC ?
一個跨多語言的 MVC Framework,其中我們要用到的是 Actionscript 3.0 版本,這是他們的官方網站。
下圖是他的架構圖,除了 MVC 外,它也用到不少其他 design pattern,第一次看可能會霧煞煞,沒關係,我們下面慢慢介紹。
Facade 可以看做是 MVC 之間的共同溝通管道,可以減少程式之間的耦合。
Command 就是扮演 Controller 的角色,PureMVC 寫出來的程式通常都會是以傳送(send)通知(Notification)的方式在進行動作,而 Command 就是負責執行它收到的通知所需要做的事情。
Proxy 從字面上看就是 Model 的代理者,不管是 C 還是 V 要取得使用資料,都必須經過它,這樣的好處是增加後端資料的可抽換性。
Mediator 可以看做是統一視覺元件的介面,透過它更新或取得視覺元件的狀態。
OK,大致上說明了 PureMVC 的概念之後,我們開始撰寫第一個程式吧,我參考了邦邦的部落格-初探pureMVC這篇文章,淺而易懂,是一個簡單的搜尋小程式,大致架構如下圖 :
範例執行結果 :
配置 PureMVC 環境 :
這裡我們使用 FlashDevelop 這套好用的免費軟體來開發,下載FlashDevelop。
接下來到 PureMVC 的網站,下載 PureMVC 的 library。
下載完可以看到 bin 資料夾裡面有個 swc 檔,待會要用的到它。
建立 Project :
安裝並開啟FlashDevelop,開啟新專案。
這裡我們選擇 AS3 Project 來開發。
專案建立完後的架構如下,之後新增的 as 檔都放在 src 資料夾下。
將剛剛下載的 swc 檔放到專案的 lib 資料夾底下。
然後右鍵加入 Library,這布記得要做唷,不然無法使用 PureMVC 的 API。
開始 Coding :
這個程式會有以下四種 Notification (通知) :
STARTUP - 程式啟動
INITED - 程式初始化結束
SEARCH - 搜尋資料
RESULT - 搜尋結果
還有三個視覺元件 :
searchTxt - 搜尋字串的輸入欄位
searchBtn - 送出搜尋字串的按鈕
resultTxt - 顯示搜尋結果的欄位
一開始我們先寫把 Model 的部分,這個 class 主要負責提供搜尋所需的資料,為了簡化學習,我們只回傳一字串 " XXX Hello PureMVC! "。
DataProxy.as :
再來我們寫 View 的部分,MainMediator 主要負責接收 INITED 跟 RESULT 這兩個通知。
INITED 代表程式初始化結束,當初始化結束的通知發出後,MainMediator 就會需要偵聽事件的視覺元件加入偵聽。
RESULT 代表搜尋結果,當這個通知發出後,MainMediator 會將搜尋結果顯示到 resultTxt 這個 TextField元件上。
MainMediator.as :
接下來我們開始寫需要用到的 Command,這裡我們先寫負責處理 SEARCH 通知的 SearchCommand。
SearchCommand.as :
再來是程式啟動 STARTUP 的 StartupCommand。
StartupCommand.as :
快結束了! 再來把程式的核心,ApplicationFacade 給完成就差不多了。
ApplicationFacade.as :
最後,將用到的視覺元件加一加,然後寫上一行 ApplicationFacade.getInstance().startup(this) 就可以收工囉!!!
Main.as :
以上是我初步學習 PureMVC 後的心得,更進階一點的介紹可以參考官方的 Best Practice 或更多相關文章。
範例原始檔:
參考文章:
最近因為工作需要用到的關係,所以又再次學習它,經過閱覽無數前輩分享的文章之後,終於初步了解它的運作模式了,因此分享這邊學習心得筆記。
什麼是 MVC ?
一個設計方法(Design pattern 也稱設計模式)。
想初步了解的朋友可以參考維基百科的說明。
網路也有許多相關文章,大略內容就是將程式開發架構分為 Model、View 和 Controller 三個部份的開發方式,建議可以閱讀深入淺出設計模式這本書。
什麼是 PureMVC ?
一個跨多語言的 MVC Framework,其中我們要用到的是 Actionscript 3.0 版本,這是他們的官方網站。
下圖是他的架構圖,除了 MVC 外,它也用到不少其他 design pattern,第一次看可能會霧煞煞,沒關係,我們下面慢慢介紹。
Facade 可以看做是 MVC 之間的共同溝通管道,可以減少程式之間的耦合。
Command 就是扮演 Controller 的角色,PureMVC 寫出來的程式通常都會是以傳送(send)通知(Notification)的方式在進行動作,而 Command 就是負責執行它收到的通知所需要做的事情。
Proxy 從字面上看就是 Model 的代理者,不管是 C 還是 V 要取得使用資料,都必須經過它,這樣的好處是增加後端資料的可抽換性。
Mediator 可以看做是統一視覺元件的介面,透過它更新或取得視覺元件的狀態。
OK,大致上說明了 PureMVC 的概念之後,我們開始撰寫第一個程式吧,我參考了邦邦的部落格-初探pureMVC這篇文章,淺而易懂,是一個簡單的搜尋小程式,大致架構如下圖 :
範例執行結果 :
配置 PureMVC 環境 :
這裡我們使用 FlashDevelop 這套好用的免費軟體來開發,下載FlashDevelop。
接下來到 PureMVC 的網站,下載 PureMVC 的 library。
下載完可以看到 bin 資料夾裡面有個 swc 檔,待會要用的到它。
建立 Project :
安裝並開啟FlashDevelop,開啟新專案。
這裡我們選擇 AS3 Project 來開發。
專案建立完後的架構如下,之後新增的 as 檔都放在 src 資料夾下。
將剛剛下載的 swc 檔放到專案的 lib 資料夾底下。
然後右鍵加入 Library,這布記得要做唷,不然無法使用 PureMVC 的 API。
開始 Coding :
這個程式會有以下四種 Notification (通知) :
STARTUP - 程式啟動
INITED - 程式初始化結束
SEARCH - 搜尋資料
RESULT - 搜尋結果
還有三個視覺元件 :
searchTxt - 搜尋字串的輸入欄位
searchBtn - 送出搜尋字串的按鈕
resultTxt - 顯示搜尋結果的欄位
一開始我們先寫把 Model 的部分,這個 class 主要負責提供搜尋所需的資料,為了簡化學習,我們只回傳一字串 " XXX Hello PureMVC! "。
DataProxy.as :
package puremvc.model { import org.puremvc.as3.interfaces.IProxy; import org.puremvc.as3.patterns.proxy.Proxy; import puremvc.ApplicationFacade; /** * DataProxy * * 儲存資料的 Model */ public class DataProxy extends Proxy implements IProxy { static public const PROXY_NAME:String = "DataProxy"; public function DataProxy() { super(PROXY_NAME, ""); } public function search(query:String):void { // 提供外部使用的搜尋 Method. // 此方法並未真正實作任何搜尋程式. // 暫時以 query + " Hello PureMVC!" 作為搜尋結果. var result:String = query + " Hello PureMVC!"; var body:Object = { }; body["result"] = result; // 將結果包裝成 body["result"] 後傳送 NOTIFICATION_RESULT 通知 this.facade.sendNotification(ApplicationFacade.NOTIFICATION_RESULT, body); } } }
再來我們寫 View 的部分,MainMediator 主要負責接收 INITED 跟 RESULT 這兩個通知。
INITED 代表程式初始化結束,當初始化結束的通知發出後,MainMediator 就會需要偵聽事件的視覺元件加入偵聽。
RESULT 代表搜尋結果,當這個通知發出後,MainMediator 會將搜尋結果顯示到 resultTxt 這個 TextField元件上。
MainMediator.as :
package puremvc.view { import flash.events.MouseEvent; import org.puremvc.as3.interfaces.IMediator; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.mediator.Mediator; import puremvc.ApplicationFacade; /** * MainMediator * * 主程式視覺介面 */ public class MainMediator extends Mediator implements IMediator { static private const MEDIATOR_NAME:String = "MainMediator"; public function MainMediator(viewComponent:Object = null) { super(MEDIATOR_NAME, viewComponent); } override public function listNotificationInterests():Array { // 此 Mediator 關注哪幾個 NOTIFICATION return [ ApplicationFacade.NOTIFICATION_INITED, ApplicationFacade.NOTIFICATION_RESULT ]; } override public function handleNotification(notification:INotification):void { // 當各 NOTIFICATION 產生通知時的動作 switch(notification.getName()) { case ApplicationFacade.NOTIFICATION_INITED: onInitedNotify(notification); break; case ApplicationFacade.NOTIFICATION_RESULT: onResultNotify(notification); break; } } private function onInitedNotify(notification:INotification):void { // 當主程式初始化結束的動作 setListener(true); } private function get app():Main { return Main(this.viewComponent); } private function onResultNotify(notification:INotification):void { // 將取得的 body["result"] 值顯示在 resultTxt 上 app.resultTxt.text = notification.getBody()["result"]; } private function setListener(boolean:Boolean = false):void { // 設定是否偵聽 searchBtn Click 事件 if (boolean) { app.searchBtn.addEventListener(MouseEvent.CLICK, onSearchBtnClick); }else { app.searchBtn.removeEventListener(MouseEvent.CLICK, onSearchBtnClick); } } private function onSearchBtnClick(e:MouseEvent):void { // 將 searchTxt 的值包裝成 body["keyword"] 後發送 NOTIFICATION_SEARCH 的通知 var body:Object = { }; body["keyword"] = app.searchTxt.text; this.facade.sendNotification(ApplicationFacade.NOTIFICATION_SEARCH, body); } } }
接下來我們開始寫需要用到的 Command,這裡我們先寫負責處理 SEARCH 通知的 SearchCommand。
SearchCommand.as :
package puremvc.controller { import org.puremvc.as3.interfaces.ICommand; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.command.SimpleCommand; import puremvc.model.DataProxy; /** * SearchCommand * * 接到搜尋通知後要做的事. */ public class SearchCommand extends SimpleCommand implements ICommand { public function SearchCommand() { super(); } override public function execute(notification:INotification):void { // 向 MainMediator 取得 body["keyword"] 對映的元件的值 var query:String = notification.getBody()["keyword"]; search(query); } private function search(query:String):void { // 將要搜尋的值傳給 DataProxy 提供的 search Method var dataProxy:DataProxy = DataProxy(this.facade.retrieveProxy(DataProxy.PROXY_NAME)); dataProxy.search(query); } } }
再來是程式啟動 STARTUP 的 StartupCommand。
StartupCommand.as :
package puremvc.controller { import org.puremvc.as3.interfaces.ICommand; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.command.SimpleCommand; import puremvc.ApplicationFacade; import puremvc.model.DataProxy; import puremvc.view.MainMediator; /** * StartupCommand * * 程式啟動時需要做的事情 */ public class StartupCommand extends SimpleCommand implements ICommand { public function StartupCommand() { super(); } override public function execute(notification:INotification):void { // 註冊 Model DataProxy this.facade.registerProxy(new DataProxy()); // 註冊 View MainMediator var app:Main = Main(notification.getBody()); this.facade.registerMediator(new MainMediator(app)); // 註冊接下來會用到的 Controller Command this.facade.registerCommand(ApplicationFacade.NOTIFICATION_SEARCH, SearchCommand); // 通知初始化結束 this.facade.sendNotification(ApplicationFacade.NOTIFICATION_INITED); } } }
快結束了! 再來把程式的核心,ApplicationFacade 給完成就差不多了。
ApplicationFacade.as :
package puremvc { import org.puremvc.as3.interfaces.IFacade; import org.puremvc.as3.patterns.facade.Facade; import puremvc.controller.StartupCommand; /** * ApplicationFacade * * 所有程式之間唯一的溝通橋樑. * 使用到 Facade 和 Singleton 設計模式. */ public class ApplicationFacade extends Facade implements IFacade { // 所有此程式會用到的列舉狀態 // - NOTIFICATION_STARTUP 程式啟動 // - NOTIFICATION_INITED 程式初始化結束 // - NOTIFICATION_SEARCH 發出搜尋的請求 // - NOTIFICATION_RESULT 產生結果的通知 static public const NOTIFICATION_STARTUP:String = "NOTIFICATION_STARTUP"; static public const NOTIFICATION_INITED:String = "NOTIFICATION_INITED"; static public const NOTIFICATION_SEARCH:String = "NOTIFICATION_SEARCH"; static public const NOTIFICATION_RESULT:String = "NOTIFICATION_RESULT"; static public function getInstance():ApplicationFacade { if (instance == null) instance = new ApplicationFacade(); return ApplicationFacade(instance); } override protected function initializeController():void { super.initializeController(); // 當 ApplicationFacade 被第一次呼叫的時候 // 會執行這個 initializeController() // 所以在這裡我們註冊 NOTIFICATION_STARTUP 這個啟動 Command this.registerCommand(NOTIFICATION_STARTUP, StartupCommand); } /** * startup * * 程式啟動. * * @param app 主程式 */ public function startup(app:Main):void { this.sendNotification(NOTIFICATION_STARTUP, app); } } }
最後,將用到的視覺元件加一加,然後寫上一行 ApplicationFacade.getInstance().startup(this) 就可以收工囉!!!
Main.as :
package { import flash.display.SimpleButton; import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldType; import flash.events.Event; import puremvc.ApplicationFacade; /** * Main * * 使用 PureMVC 來實作一個簡單的搜尋小程式. */ public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point initComponents(); // pureMVC 的進入點. // 使用唯一的 ApplicationFacade class 作為入口. // 之後所有的元件之間溝通都需要經過此 facade. ApplicationFacade.getInstance().startup(this); } public var searchTxt:TextField; public var searchBtn:SimpleButton; public var resultTxt:TextField; private function initComponents():void { // 初始化視覺元件(ViewCompoents) // searchTxt searchTxt = new TextField(); searchTxt.type = TextFieldType.INPUT; searchTxt.border = true; searchTxt.x = 50; searchTxt.y = 50; searchTxt.width = 200; searchTxt.height = 20; this.addChild(searchTxt); // searchBtn var buttonStyle:Sprite = new Sprite(); buttonStyle.graphics.beginFill(0xcccccc); buttonStyle.graphics.drawRect(0, 0, 100, 20); buttonStyle.graphics.endFill(); var buttonTxt:TextField = new TextField(); buttonTxt.text = "Search Button"; buttonTxt.width = 100; buttonTxt.height = 20; buttonStyle.addChild(buttonTxt); searchBtn = new SimpleButton(buttonStyle, buttonStyle, buttonStyle, buttonStyle); searchBtn.x = 260; searchBtn.y = 50; this.addChild(searchBtn); // resultTxt resultTxt = new TextField(); resultTxt.type = TextFieldType.DYNAMIC; resultTxt.border = true; resultTxt.x = 50; resultTxt.y = 80; resultTxt.width = 200; resultTxt.height = 200; this.addChild(resultTxt); } } }
以上是我初步學習 PureMVC 後的心得,更進階一點的介紹可以參考官方的 Best Practice 或更多相關文章。
範例原始檔:
參考文章:
有沒興趣Survey一下Model的實作, 我蠻好奇那邊會長怎樣XD
回覆刪除