Sep 14, 2010

[Flex] PureMVC standard with Spring extensions

由於上次稍微玩了一下 Robotlegs 依賴注入(DI) 主導的 MVC 框架,而著名也使用依賴注入的 Java / Java EE 的 Spring framework 出了 for ActionScript 的版本,剛好在最近 Spring ActionScript 1.0 正式 release 了(想了解 Spring 是啥咪東東的話請自行找 google 大神),這個版本除了基本框架外,也包含了 Cairngorm 與 PureMVC 的外掛...想當然耳,就拿來測試一下用在 PureMVC 內的感覺囉!!

參考了官方範例 中 PureMVC 唯二的範例原始檔,以下使用的是「設定檔依賴注入 facade 透過 addConfigSource() 的方式來 init 」:(其實除了 embed 外,都是外部載入)

Online Demo with source code

工作環境:FlashBuilder, Flex SDK4
  1. 請下載 PureMVC Standard 版本
  2. 再下載 Spring ActionScript 最新版本後,除了 spring-actionscript-cairngorm 不需要外,都放到 /src 下(記得只需要 org 開始...),也別忘了lib 內的 swc 檔 copy 到 /libs 下

Spring 的 injection 並不像 Robotlegs 直接來個 [Inject] metadata 的自動化那樣方便,但是其冷血度(檔案的鬆偶程度)更勝後者!如果你要使用設定檔(applicationContext.xml) 來做注入的話,準備工作就挺多的...XD
  1. 依照 applicationContext.xml 內設定的方式分別寫入 constructor 或者是 setter 依賴注入(本範例統一使用 setter injection)
  2. 為了跟大家都沒關係所以都使用 interface 來處理,所以你會在範例中發現大家都有介面...(並沒有真的研究過 Spring,也許還有其他作法)
  3. 準備 compiler 時候要用的 classe。由於在 setter, getter 的寫法上都使用 interface,所以真正用到的 class 需要預先在輸出階段就打包到程式內。
  4. 基本上 PureMVC 類 class 都改為繼承自 IocSimpleCommand, IocFacade, IcoMediator and IocProxy.


application-context.xml :
程式內用到的 Ioc 系列 class 的 getConfigName() 就是指 object tag id。

........
<object id="updateListCommand" class="com.mvc.control.UpdateListCommand">
<property name="listProxy" ref="listProxy"/>
</object>
<object id="listProxy" class="com.mvc.model.impl.ListProxy">
<property name="loaderDelegate" ref="loaderDelegate"/>
</object>
........


設定依賴注入的 setter name, ref 為 object tag 的 id。
其他 xml schemas 設定請看官網教學文件。
<property name="listProxy" ref="listProxy"/>

LoaderListCommand 實作:

........
public class UpdateListCommand extends IocSimpleCommand
{
private var m_proxy:IListProxy;
public function UpdateListCommand()
{
super();
}
// setter injection
public function set listProxy( proxy:IListProxy ):void{
m_proxy = proxy;
}
public function get listProxy():IListProxy{
return m_proxy;
}
...........

接下來在 compiler config 的 xml 中列出所有要預先打包的 classes:
configurationClassesCompilerConfig.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flex-config>
<includes>
<symbol>com.mvc.control.StartUpCommand</symbol>
<symbol>com.mvc.control.UpdateListCommand</symbol>
<symbol>com.mvc.view.ListMediator</symbol>
<symbol>com.mvc.model.impl.ListProxy</symbol>
<symbol>com.mvc.delegate.impl.LoaderDelegate</symbol>
</includes>
</flex-config>

然後:在 Project/properties 中設定 Additional Compiler Arguments:

-load-config+=config/configurationClassesCompilerConfig.xml

如下圖位置:

基本上的設定就是如同上面步驟。

最後,範例檔內有兩個外部 list data xml,你可以試著修改 application-context.xml 中的 serviceUrl value to "List2.xml"

<object id="loaderDelegate" class="com.mvc.delegate.impl.LoaderDelegate">
<property name="serviceUrl" value="List2.xml"/>
</object>

重新 reload 後你會發現,這時候 LoaderDelegate 所載入的資料就是 List2.xml 囉!如果你預先將同類可置換的 class 打包起來,你也可以在 runtime 的時候修改 application-context object class ref,這就是 Ioc 強大的地方囉!

結語:
以上僅說明到 PureMVC with Spring extensions 部分,其餘 Spring ActionScript Core API 請自行上官網學習囉!
BTW...對它不熟的朋友們請勿輕易服用拿真正的專案去實作捏...XD

Sep 7, 2010

Robotlegs AS3 MVC framework in Flex project (II)



這次的範例是兩組 viewComponent 共用同一個 Mediator 外加不同的 instance:
Source code download


要共用的重點就是介面(Interface),所以你會發現這次的兩組 viewComponent 都有實作 IMessageBox
IMessageBox:

public interface IMessageBox
{
function update( newline:String ):void;
}

然後在兩組的 viewComponent 中實作 IMessageBox

<s:Group ....
implements="IMessageBox"
>

<fx:script>
.....
public function update( newline:String ):void{
//IMessageBox interface 實作
data.addItem( newline );
}
.....
</fx:Script>


接下來修改的部份為 MessageBoxMediator

....
[Inject]
public var view:IMessageBox;
//同樣注入屬性...這次記得改成 interface. 因為此 Mediator 為共用
....


接下來在 RobotLegContext startup() 中修改:

....
override public function startup():void
{
//bootstrap here
//auto start, injectViewAs 寫入 viewComponent interface
mediatorMap.mapView( MessageBox, MessageBoxMediator , IMessageBox );
mediatorMap.mapView( MessageBox1, MessageBoxMediator , IMessageBox );
}
....


你會發現 mapView 的 injectViewAs:Class 填入 interface class, 這時候 Robotlegs 的核心在比對 class 的時候才會以 IMessageBox 為主,否則 Mediator 將無法共用,因為在 MessageBoxMediator [Inject] 的 view 為 IMessageBox, 而不是各自實作的 class。

規劃 viewComponent 的共用性也需要考量一下,不然很有可能會有 interface 滿天飛的狀況喔!
以上範例都沒說明到 Command, Proxy and Service ,有興趣的朋友請自行到官網上去找尋相關的教學文章囉!

結論:
Robotlegs 真的很適合懶人 + AS3 MVC 新手學習,不過 Robotlegs 在背地裡做了很多事情,尤其自動化這點雖然方便但是也出現一些奇怪的現象,所以掌控啟動順序可能會比直接用自動化來的安全些...慶幸的是 Robotlegs 程式彈性相當大,非自動化操作起來也是相當的方便。 =)

Sep 6, 2010

Robotlegs AS3 MVC framework in Flex project (I)

由於今天在 奶綠茶的 Robotlegs FlashAS3 MVC framework 看到他讚賞 Robotlegs AS3 MVC framework 的好用,不禁好奇的來研究一番...

其實這個 AS3 MVC framework 出現有些時日,但是一直都沒有特別的關注它。簡單看完 Robotlegs 最佳實踐,其實就可以大部分的了解 Robotlegs 運作的邏輯。不如其他的 MVC framework 有很多語言版本, Robotlegs 完全是專注在 AS3 上。也由於僅有一個版本,所以訊息傳遞是利用 AS3 Event 機制來完成,對於一些想要入門實作 MVC 的 Flash 工程師來說絕對是比 pureMVC 好入門很多...=)


Robotlegs 的重點就是搭著「 依賴注入( Dependency Injection) 」達成 MVCS 的架構,  擷取用最佳實踐的中文翻譯解釋:

Quote:
最簡單地, 依賴注入是為對象提供實例變量或屬性的行為. 當你傳遞一個變量到一個類的構造函數, 你在使用依賴注入. 當你設置一個類的屬性, 你在使用依賴注入. 如果你不是使用嚴格的過程或線性方式編寫AS3, 很可能你現在就在使用依賴注入。

呃...依賴注入? ?簡單來講一台霜淇林機,你要賣什麼口味的霜淇林就是依賴不同的原料放置到左右兩個攪拌桶內,而每一個口味可以透過介面實作來達成依賴注入。
以下是簡單的 Dependency Injection 的 Setter injection 範例:

public interface 霜淇淋介面
{
function 擠():void;
}

然後實作:

public class 巧克力 implements 霜淇淋介面{
public function 擠():void{
trace("巧克力口味的霜淇淋");
}
}
public class 草莓 implements 霜淇淋介面{
public function 擠():void{
trace("草莓口味的霜淇淋");
}
}
public class 香草 implements 霜淇淋介面{
public function 擠():void{
trace("香草口味的霜淇淋");
}
}

霜淇淋機台

public class 機台{
public var 左邊桶的霜淇淋:霜淇淋介面;
public var 右邊桶的霜淇淋:霜淇淋介面;

public function set左邊桶口味( obj:霜淇淋介面 ):void{
左邊桶的霜淇淋 = obj;
}
public function set右邊桶口味( obj:霜淇淋介面 ):void{
右邊桶的霜淇淋 = obj;
}
public function 擠綜合():void{
左邊桶的霜淇淋.擠();
右邊桶的霜淇淋.擠();
}
}

測試:

var 機台A:霜淇淋機台 = new 霜淇淋機台();
機台A.set左邊桶口味( new 巧克力 );
機台A.set右邊桶口味( new 香草 );
機台A.擠綜合(); // 香草巧克力~~

var 機台B:霜淇淋機台 = new 霜淇淋機台();
機台B.set左邊桶口味( new 草莓 );
機台B.set右邊桶口味( new 香草 );
機台B.擠綜合(); // 香草草莓


回到 Robotlegs 它用的是 轉接器 (adapter) 映射注入,就是將原料注入到一個反射庫轉接器 (SwiftSuspenders) 來用...反正就是一堆東西塞到核心反射庫轉接器內,程式用到時候自動幫你轉換取用...懶人最愛?

以下用一個很爛的範例來說明 Robotlegs 的 Injection 機制...
反正範例照貼就會出現下面的畫面~~

其實 Robotlegs 用法跟 pureMVC 很像,它也有使用 Command, Mediator, Proxy,但是又比 pureMVC 簡約很多...

先下載 最新版 Robotlegs
,解壓後 src 內的直接放到 Flex Project 的 src 下,SwiftSuspenders.swc 直接塞到 libs 下。

在 FlashBuilder 中開一個 Flex project names "RobotlegsInjectTest"

第一,做一個 MessageBox 的 mxml component 放到根目錄下:Robotlegs 的重點就是儘量不動你的 Flash/Flex class...

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:VerticalLayout />
</s:layout>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var data:ArrayCollection;

private function init():void{
data = new ArrayCollection;
data.addItem("My instance name is "+ id );
}
public function update( newline:String ):void{
data.addItem( newline );
}
]]>
</fx:Script>
<s:List dataProvider="{data}" width="200" height="200" />
<s:Button label="Broadcasing" click="dispatchEvent(new Event('btnClick'))" />
</s:Group>


再來寫 MessageBox 用的 Mediator class:跟 pureMVC 一樣, viewComponent 歸 Mediator 操作

package
{
import flash.events.Event;
import org.robotlegs.mvcs.Mediator;

public class MessageBoxMediator extends Mediator
{
[Inject]
public var view:MessageBox;
//直接注入屬性,反正轉接器會自動幫你轉~~

public function MessageBoxMediator()
{
super();
}
override public function onRegister():void{
eventMap.mapListener( view , "btnClick" , onClick );
/*加入事件監聽的寫法
* 為什麼不實作 onRemove ? 機器腳體諒大家
* onPreRemove 就將一些 listener 都自動清乾淨了...懶人指數 + 1
*/這也是專攻一種語言的好處...=)
}
private function onClick(event:Event):void{
switch( event.type ){
case "btnClick":
view.update( "lalalala" );
break;
}
}
}
}


來做 Application 用的 Context...真的很類似 pureMVC 中的 Facade

package
{
import flash.display.DisplayObjectContainer;

import org.robotlegs.mvcs.Context;

public class TestRobotLegContext extends Context
{
public function TestRobotLegContext(contextView:DisplayObjectContainer, autoStartup:Boolean=true)
{
super(contextView, autoStartup);
}
override public function startup():void
{
//bootstrap here
// 預設自動化
//意思就是注入 mapping 後會自動幫你 on MessageBoxMediator...懶人指數 +1
mediatorMap.mapView( MessageBox, MessageBoxMediator );
}
}
}

最後在 RobotlegsInjectText.mxml 中寫上:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:views = "*"
minWidth="300" minHeight="300" creationComplete="new TestRobotLegContext(this)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label text="Robotlags framework demo 1" x="5" y="10" fontSize="16"/>
<views:MessageBox id="View1" y="40" x="10" />
</s:Application>

 

creationComplete="new TestRobotLegContext(this)"
//啟動 TestRobotLegContext


結束了~~~~~四個檔案就可以完成基本使用...也不怪乎一堆人對它的簡單入門推崇,懶人指數 X N!!!
以下有個很好的範例:
Shaun Smith 的 RobotLegs AS3: A DI Driven MVCS Framework for Flash & Flex – Inspired by PureMVC

後話:
因為目前並沒有實作過 Robotlegs 框架的中大型專案,所以沒辦法真正比較與 pureMVC 的優劣,但是看到目前為止, Robotlegs 對於想要入門實作 AS3 MVC framework 的不外乎是一個很好的選擇。尤其它與 pureMVC 有很多地方相似,對於想要學習 pureMVC 的朋友也可以是一個簡單入門的跳板。最後,想要 Flash 版的教學範例可以等奶綠茶老師的 Robotlegs 教學文章囉!

to be continue...=P

[App] 國道計程收費速算器 2.0

之前的版本跟目前 國道計程官網 所列資料誤差很大,這支 App 也一直有人在使用,所以為了答謝愛用者只好做了一次大更新,因為對 CoronaSDK scrollView widget 有點怨言,所以 iOS 版本採用 Swift3 重寫了一遍,不過 Android 版本還是維...