Micro Architecture ─ PureMVC Part1

PureMVC架構比較複雜,整理幾篇學習的心得跟大家分享

PureMVC跟Cairngorm一樣是幫助程式設計師在設計程式時,能有效分出Model、View、Controller的架構,比較特殊的是,PureMVC提供了多port版本,也就是除了Flex/Flash外,PureMVC也可用在C#、C、Java等其它語言上,使用非常地廣範(學起來不吃虧 :-)),而在AS3語法,PureMVC分成兩個版本 ─ Standard跟MultiCore,Standard是指一般的PureMVC架構,把程式分成了Facade、Proxy、Command跟Mediator,而MultiCore則是允許一個專案有多核心的PureMVC架構,每個架構都有自己的Facade,彼此不會衝突,MultiCore在其它語言較少用到,在Flex可做為Module的應用,之後會再寫一篇介紹。

PureMVC由幾個主要元件構成,元件包括:

  1. Facade:Singleton,每個專案只有一個,用來管理Model、View及Controller之間的溝通,可處理註冊(Register)、取得(Retrieve)及刪除(Remove) 元件
  2. Notification:取代原架構的Event,用來在元件間傳遞訊息(Nitification可帶一個Body物件跟一個Type字串)
  3. Mediator:用來管理View Component的操作,可接收及發送Notification
  4. Proxy:用來管理及操作資料,只發不收Notification(資料更新時,發送更新訊息),若要存取遠端資料,可再利用Delegate的方式,把遠端操作封裝起來,提升程式再用性
  5. Command:用來管理應用程式的Business Logic,協調Model跟View,本身是無狀態,只有在需要時才被創建,執行完後即刪除

官方網站 http://puremvc.org/

Example - Flex AS3 Standard PureMVC

程式架構

2009-03-12_205049.jpg

範例為簡單的RSS Reader ,可讀取XML及刪除清單資料

程試的入口是main,我們先生成Facade,並在main載入完成後設定Facade的初值:applicationComplete="facade.startup(this);" (把Main的參照傳進去)





<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
applicationComplete="facade.startup(this);"
fontSize="12"
pageTitle="PureMVC Test Page"
xmlns:view="idv.gd.pureMVCTest.view.components.*">

<mx:Script>
<![CDATA[
import idv.gd.pureMVCTest.view.ApplicationMediator;
import idv.gd.pureMVCTest.ApplicationFacade;

private var facade:ApplicationFacade = ApplicationFacade.getInstance();
]]>
</mx:Script>
<view:RSSReader id="reader" />
<mx:Button id="newsBtn" label="Read News" x="112" y="408"/>
<mx:Button id="techBtn" label="Read Tech" x="10" y="408"/>
</mx:Application>


Facade可視為PureMVC的總管,Proxy、Mediator及Command都可以透過Facade向其它元件存取





package idv.gd.pureMVCTest
{
import idv.gd.pureMVCTest.controller.ApplicationStartupCommand;
import idv.gd.pureMVCTest.controller.DeleteDataCommand;
import idv.gd.pureMVCTest.controller.ReadRSSCommand;

import org.puremvc.as3.patterns.facade.Facade;

public class ApplicationFacade extends Facade
{
public static const STARTUP:String = "startup";

public static const RSS_LOAD_COMPLETE:String = "rssLoadComplete";
public static const RSS_LOAD_FAULT:String = "rssLoadFault";

public static const DELETE_ITEM:String = "deleteItem";
public static const DELETE_ALL:String = "deleteAll";

public static const READ_NEWS:String = "readNews";
public static const READ_TECH:String = "readTech";

/**
* 在controller生成後,加入起始事件
*/
override protected function initializeController():void
{
super.initializeController();

this.registerCommand(STARTUP, ApplicationStartupCommand);
this.registerCommand(DELETE_ITEM, DeleteDataCommand);
this.registerCommand(DELETE_ALL, DeleteDataCommand);

this.registerCommand(READ_NEWS, ReadRSSCommand);
this.registerCommand(READ_TECH, ReadRSSCommand);

}

public static function getInstance():ApplicationFacade
{
if (instance == null)
instance = new ApplicationFacade();

return instance as ApplicationFacade;
}

/**
* 起始程式,傳入起始的 Application
*/
public function startup(app:Main):void
{
this.sendNotification(STARTUP, app);
}

}
}


在Facade裡先註冊要用到的Command(集中管理,類似Cairngorm的FrontController,指示Notification要執行那個command),其中有一個以Startup命名的Command是程式的進入點,這個Command會去註冊其它要用到的Mediator及Proxy,當然不叫Startup也可以,不過官方都建議這樣寫了,還是照寫吧,提升可讀性,這個命令是在Main.mxml生成完後調用,透過Notification觸發

Command有分兩種,MacroCommand及SimpleCommand,顧名思義,Macro就是可執行多命令的Command,Simple就只執行單一命令,為了練習,範例在ApplicationStartupCommand去執行ModelPrepCommand及ViewPrepCommand兩個Command,這兩個Command有順序性,照PureMVC的說法,第一個執行完後才會執行下一個

範例註冊Proxy及Mediator,並在Mediator生成後給調用Proxy取值





package idv.gd.pureMVCTest.controller
{
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.MacroCommand;

public class ApplicationStartupCommand extends MacroCommand
{
//MacroCommand 的進入點
override protected function initializeMacroCommand():void
{
//MacroCommand 可執行多 SimpleCommand,生成有順序性,notification向下傳遞
this.addSubCommand(ModelPrepCommand);
this.addSubCommand(ViewPrepCommand);
}
}
}






package idv.gd.pureMVCTest.controller
{
import idv.gd.pureMVCTest.model.RSSProxy;

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

/**
* 生成資料 proxy
*/
public class ModelPrepCommand extends SimpleCommand
{
//SimpleCommand 的進入點
override public function execute(notification:INotification):void
{
this.facade.registerProxy(new RSSProxy());
}
}
}






package idv.gd.pureMVCTest.controller
{
import idv.gd.pureMVCTest.model.RSSProxy;
import idv.gd.pureMVCTest.view.ApplicationMediator;

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

/**
* 生成 mediator,實際向proxy取值
*/
public class ViewPrepCommand extends SimpleCommand
{
//SimpleCommand 的進入點
override public function execute(notification:INotification):void
{
var mainApp:Main = Main(notification.getBody());
var rssProxy:RSSProxy = this.facade.retrieveProxy(RSSProxy.NAME) as RSSProxy;

this.facade.registerMediator(new ApplicationMediator(mainApp));

rssProxy.getRealNews();
}

}
}



篇幅有點大了,先介紹到這,下一篇再來介紹Mediator跟Proxy

0 意見 :: Micro Architecture ─ PureMVC Part1