Skip to main content

[Flex] pureMVC Standard 練習筆記

參考資料 : pureMVC 官方網站

也許對於"純"程式設計師來說 pureMVC 才是比較正統的 MVC design pattern 的作法...跟它相處了兩天還是沒辦法對它產生愛啊... 也許它的可攜性比較高...但是我實在看不到它可愛在哪裡...也許它太正統了;也許是因為它都不使用 Flex framework吧...(可能需要慢慢體會...)

先了解它的主要架構就是一對一對的:

• Model vs. Proxy
• View vs. Mediator
• Controller vs. Command

以下是同上篇 [Flex] Cairngorm 練習筆記 一樣的工作改為 pureMVC 寫法
Menu tree:


list.xml :

<?xml version="1.0" encoding="utf-8"?>
<data>
<list name="AAAA" data="0"/>
<list name="BBBB" data="1"/>
<list name="CCCC" data="2"/>
<list name="DDDD" data="3"/>
</data>


0. ListVO
package com.mvc.model.vo
{
public class listVO
{
public function listVO(label:String, data:String){
this.label = label;
this.data = data;
}
public var label:String;
public var data:String;
}
}


1. 從 ListProxy 開始寫
pureMVC 的 Proxy 主要的工作大概就是:
  • 匯集對應 Models 提供方法、屬性給別人使用 ( 建議:給自己或 Command 修改就好 )

  • 要努力跟 Mediator 絕交,不要認識最好

  • 封裝區域邏輯 ( 反正就是弄好資料才給別人 )

  • 只發不收 Notification

package com.mvc.model
{
import mx.rpc.IResponder;
import mx.rpc.http.HTTPService;
import mx.collections.ArrayCollection;
import com.mvc.model.vo.listVO;

import org.puremvc.as3.patterns.proxy.Proxy;

public class ListProxy extends Proxy implements IResponder
{
public static const NAME:String = "listProxy";
public static const LIST_CHANGED:String ="list_changed";

public function ListProxy()
{
super(NAME, new ArrayCollection);
update();
}
public function update():void{
var httpService:HTTPService = new HTTPService();
httpService.url="data/list.xml";
httpService.resultFormat = "e4x";
var call:Object = httpService.send();
call.addResponder(this);
}
//隱藏的 getter 給自己看的,順便轉換資料型態
private function get list() : ArrayCollection {
return data as ArrayCollection;
}
/**
* Implemented mx.rpc.IResponder
* 外面的別用這兩個方法
*/
public function result( event:Object ):void{
var xml:XML = XML(event.result);
var list:ArrayCollection = new ArrayCollection;
for each (var subxml:XML in xml.list){
list.addItem( new listVO ( subxml.@name, subxml.@data));
}
setData(list);
//改完就要發 Notification
sendNotification( LIST_CHANGED, data );
}
public function fault( obj:Object ):void{
trace("XML 載入錯誤");
}
}
}


2. ListMediator:對應 List Component 在主要的 mxml 上
Mediator 主要的工作有:
  • 監聽並反應 viewComponent 發送的 Events

  • 可收發與處理 Notifications

  • 避免直接修改 Proxy ,要嘛就是用 Command

  • 其他?...( 跟它還不夠熟 )

package com.mvc.view
{
import com.mvc.model.ListProxy;

import mx.collections.ArrayCollection;
import mx.controls.List;

import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;

public class ListMediator extends Mediator
{
public function ListMediator(mediatorName:String=null, viewComponent:Object=null)
{
super(mediatorName, viewComponent);
}
override public function listNotificationInterests():Array
{
//要處理啥訊息都在這邊列好
return [
ListProxy.LIST_CHANGED
];
}

override public function handleNotification(notification:INotification):void
{
//上面有列這邊才收的到喔!
switch(notification.getName()){
case ListProxy.LIST_CHANGED:
list.dataProvider = notification.getBody() as ArrayCollection;
}
}
//給自己看的,重點還是轉換物件型態
private function get list():List {
return viewComponent as List;
}

}
}


3. ApplicationFacade:MVC 的總代理,它當然是只有一個
所有的 Proxies , Mediators 跟 Commands 註冊、取用與毀滅都在這邊,不過為了"儘量"跟別人沒關係,它這邊除了 Commond 的註冊外,其餘都是寫在 StartupCommand 內。以下幾乎是 ApplocationFacade 的標準寫法。

package com.mvc
{
import org.puremvc.as3.patterns.facade.Facade;
import org.puremvc.as3.interfaces.IFacade;
import com.mvc.control.StartupCommand;

public class ApplicationFacade extends Facade implements IFacade
{
public static const STARTUP:String = "startup";
public function ApplicationFacade()
{
super();
}
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( app:TestPureMVC ) : void
{
sendNotification( STARTUP, app );
}
}
}

反正就是先註冊了一個 StartupCommand 然後馬上 Notification 它~~

4. StartupCommand
該初始化的都在這邊完成~~
package com.mvc.control
{
import com.mvc.model.ListProxy;
import com.mvc.view.ListMediator;

import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.SimpleCommand;

public class StartupCommand extends SimpleCommand
{
public function StartupCommand()
{
super();
}
override public function execute(notification:INotification):void{
//這邊只控制畫面上的list component...- - 它的 id = list
var list:Object = notification.getBody().list;
facade.registerMediator( new ListMediator(null, list) );
facade.registerProxy( new ListProxy());
}
}
}


5. TestPureMVC.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="220" height="200" layout="absolute"
applicationComplete="facade.startup( this )" >
<mx:Script>
<![CDATA[
import com.mvc.ApplicationFacade;
private var facade:ApplicationFacade = ApplicationFacade.getInstance();
]]>
</mx:Script>
<!-- ListMediator 的控制對象-->
<mx:List id="list" />
</mx:Application>


到目前的理解,pureMVC 事件傳遞機制是採用 Observer vs. Notification。如果有註冊成組的 Command 與 Notification ,Notification 被發送時也會執行 Command,然後 viewComponent 採用 as3 的 Event 機制發送 Event 給 Mediator 監聽...反正就是多用 Notification & Command吧...

to be continue....?

Comments

  1. 感謝Erin大大的教學~

    看來對於龐大的項目,還是要先找個比較適合的pattern歸宿啊
    個人感覺Flex framework的UI部分太臃腫了,而且又難Hack,那些API足以讓我抓狂一天
    還是比較喜歡“純”AS project,light-weight的東西,不知哪里有更優的UI呢

    ReplyDelete
  2. 我倒蠻喜歡 Flex 的 mxml 編寫方式...XD 好適合懶人啊...

    ReplyDelete
  3. mxml 不錯!!!
    其實自訂的方式還滿容易的
    (感覺上...

    pure有點難啃...
    多啃幾次

    ReplyDelete
  4. registerCommand的時機
    只能在ApplicationFacade裡面嗎?

    如果有非常多的Controller設定的數量...
    好像會滿可觀的

    ReplyDelete
  5. registerCommand 並不是一定得在 ApplicationFacade 裡喔!

    ReplyDelete
  6. 很好入門的一篇教學~讚!!

    ReplyDelete

Post a Comment

Popular posts from this blog

PureMVC 我也會 [0]

最近感覺 PureMVC 又熱了起來,也剛好好久沒有更新文章了, 就順便將去年底做的企業內訓 PureMVC 課程部分整理寫出來, 要講 PureMVC 當然要先從啥是 MVC 講起: Model-View-Control 出處: 維基百科 MVC ,大概節錄一段: (控制器Controller)- 負責轉發請求,對請求進行處理。 (檢視View) - 介面設計人員進行圖形介面設計。 (模型Model) - 程式設計師編寫程式應有的功能(實作算法等等)、數據庫專家進行資料管理和數據庫設計(可以實作具體的功能)。 其實到 Flash 的世界來講,Model and Control 都是由 .as 處理,而 View 便是 .fla+.as ,為了要鬆綁之間的關係,Event 機制就相當重要。其實每個人對 MVC 的最佳解釋都不同,真的要多練習才會有所領悟。 簡單來說: Model = 餐廳廚房 data: 西餐類 action:依照點菜單做餐點 action: 做完餐點就是將餐點放在出菜口按下通知鈴等服務生來 Control = 服務生 action: 聽到大門歡迎鈴就要說「歡迎光臨」 action: 看到客人揮揮手要去收點菜單 action: 聽到廚房通知鈴看是哪桌的餐點去送菜 View = 餐廳外場 view: 田園式的西餐廳裝潢 action: 客人進門會有歡迎鈴 action: 客人揮揮手叫服務生過來服務,是哪個服務生都無所謂,重點只要會收點菜就行了。 action: 客人收到餐點準備開動 當餐廳要改成外炒店,這時候只需要將大廚換成會中餐廚師,其出的菜就是中式快炒。 當餐廳外場由田園式外觀重新裝潢成華麗感夜店風,其進門的客層也會有所不同。 重點就是當你換掉一個地方時,對其它的部份不會造成太大的影響或者根本無所謂,這就是 MVC 所講求的境界... 一般來說,小專案有沒有必要使用 MVC 就是由各位自己判斷了,當你習慣將程式切分開來,發現 debug 不是一件痛苦的事情時,這時候有沒有強制使用 MVC 倒不是重點,因為你已經養成良好的撰寫習慣。但是開始接觸大型專案配合 team work 時,在沒有一個共用的核心框架前提下,這個專案開發到最後一定會是一個多手多腳的怪物,共用核心框架的價值就在這邊展現,這

[Swift3] weak 與 unowned 關鍵字

雖然在 Swift 中看起來"很像"是不需要煩惱內存管理的問題,不過實際上它還是遵循著自動引用計數 (ARC) 的規則,當一個物件沒有被其他對象引用時會自動被銷毀,如果三魂七魄沒有完全回位的話,就會有個靈體留在現世的空間裡,最經典的範例如下: 閉包(Closure)引用 classClassA { typealias Complete = ()->() var name : String var onComplete : Complete? init(_ name: String){ self.name = name print("Hello I am \(self.name)") onComplete = { print("\(self.name): onComplete!") // --> 閉包引用 self, 計數 + 1 } } deinit { print("deinit: \(self.name)") } } var a : ClassA? = ClassA("A") // --> 引用計數 + 1 a = nil // 2-1 = 1 還剩下 1 所以沒辦法銷毀 ---output------- Hello I am A 由於這邊的 onComplete 宣告為 Optional, 正確的做法要連同 onComplete 一起刪除才可以被回收,若不是 Optional 則會進入無法回收狀態: var b : ClassA? = ClassA("B") b?.onComplete = nil // --> 還好是 Optional 可以設成 nil 計數 - 1 b = nil // 計數 = 0 所以被回收 ---output------- Hello I am B deinit: B 但是做人不需要煩惱太多,這時候就出動 unowned 關鍵字讓物件可以順利被回收: onComplete = { [unowned self] in print