Jan 19, 2009

[Flex] Cairngorm v.s. pureMVC

跟 Cairngorm and pureMVC 相處過一陣子之後,在這邊與大家分享一下使用它們在 Flex 中的開發經驗(也許有錯誤的理解,請多指正囉!)

Cairngorm :
喜歡的部份:
  • 架在 Flex framework 上使用中央 Event (CairngormEventDispatcher )機制,UI 開發簡單 (因為容易理解...)
  • ModelLocator 採用 Flex 綁定機制,UI 顯示資料無煩惱
  • 由於上面兩項,所以使用 Module 開發專案不會很痛苦
    討厭的部份:
    • 主要的東西都是 Singleton design pattern, 如果專案很大的話,又統一在一起開發,那個 ModelLocator 到後來應該會很奇怪...
    • 綁太多細節在 UI component 內,如 UI component 一定要認識 ModelLocator 還可以直接操作或修改它...@@
    • Event 跟 Command 比多的...重點跟 pureMVC 的 command 比起來它的亂多了...
    • 專案越大越亂...
    pureMVC:
    喜歡的部份:
    • 大家的職責切分的很乾淨...棒 (這個只能意會不能言傳啊...XD 快去研究它吧!)
    • 寫在 UI (MXML)內的程式開發起來比使用 Cairngorm 的乾淨好幾倍(因為邏輯層被切分到 Mediator 了)
    • Notification & Command 果然是好物...雖然一開始感覺很囉唆,但是跟它熟了之後發現真是好用...
    • Proxy 比 ModelLocator 更加好用...因為不需要的 Proxy 要清掉也很容易...所以在資料的增減上,pureMVC 架構彈性比較高
    • 簡而言之就是乾淨乾淨乾淨~~~
    討厭的部份:
    • 它的架構比較像仙人一樣超脫於 Flex framework 之上,所以想要整合 Flex 的優點要花點腦袋想(結果這個也是優點...@@)
    • 很多語法都是要繼承並 override 寫起來不夠爽...快,這點 Cairngorm 大量的使用 interface 就棒多了。
    • 由於 Facade 的存在如果要使用 Module 與 MultiCore 版本來開發的話,還需要撰寫連接的元素,或者直接參考 pureMVC網站上的 Utility - AS3 / Pipes 來應用...不過我覺得加上 Pipes 的 UI component 就喪失了它的簡單美,可能會想別的方法來解決它...XD
    看到這邊大家應該蠻容易理解 Erin 最後的選項吧...ccc

    Jan 16, 2009

    [LEGO] How to make an "Unique Random Number" my Block


    這篇是答應某人要公開分享的教學...這兩天終於湊出時間來寫...

    Erin 分享的原則通常都秉持著 "給你魚吃,到不如教你釣魚" 的想法在寫,所以如果沒有附上原始檔也請不用詢問了呦...意思就是我已經提供很多可以讓你"開始學"釣魚的概念囉!

    這是去年三月在台北舉辦的 Flex&AIR AUG 網聚上使用的 lotto-bot 中的取得唯一亂數號碼 myBlock,可以用在樂透遊戲或者是亂數只給出現一次的程式應用。它的邏輯很簡單,只是要改成 NXT-G 的 myBlock 花了些時間連連看...XD

    開始製作之前請先注意看看你的 NXT-G 有沒有安裝過 Dynamic Block Update ,安裝完成後可以在下拉式功能表中找到 "Block Import and Export Wizard"。接下來請下載及 import Simple Array Block,在下載的時候請記得點一下畫面中的google AD 感謝 Simple Array 的作者 Steve Hassenplug 提供了很多很棒的NXT-G Blocks 給大家使用。

    由於 NXT-G 的變數是全域變數,所以在組合 myBlock 要小心變數命名,取得唯一亂數號碼需要兩個 myBlock ,一個負責初始化相關參數,一個負責輸出唯一號碼:
    initUniqueRandom myBlock:
    1. var uniq_total:Number = total;

    2. Simple Array Block : clear

    3. Add a value(e.g. 100) to index 0 in Simple Array Block, coz we don't use index 0. If you need it to count from 0, just ignore this step.


    如圖:


    接下來就麻煩了...很長一串...

    getUniqueRandom myBlock:
    這段要用程式表示囉...不然寫的好累...冏...
    var Logic 1 : Logic = false;
    //Logic Switch
    if(uniq_total == uniq_legth){
    //Logic Switch Conditions : True
    Logic 1 = true;
    // output isEnd
    }else{
    //Logic Switch Conditions : False

    //Logic Loop Until isSame = False
    do Loop1(!isSame){
    var isSame:Logic = false;
    uniq_result = Random(1, uniq_total);
    //Logic Loop Until count == uniq_length
    for(count = 0; count <= uniq_length ; count ++){
    if(uniq_result == SimpleArray[Value_index]){
    isSame = true;
    //if isSame = true, will do Loop1 again
    }
    }
    // coz isSame = false , so break loop
    }
    SimpleArray[Value_index] = uniq_result;
    uniq_length = uniq_length + 1;
    }

    output Logic 1 as isEnd;
    output uniq_result as result;

    圖示:


    如何使用:
    在遊戲一開始放置 initUniqueRandom 並傳入 total ,將 getUniqueRandom 放在Logic Loop直到 isEnd = true 跳出 Loop
    這樣在 Logic Loop 內就可以一直輸出唯一的亂數囉!

    Enjoy~

    Jan 8, 2009

    [Flex] pureMVC 練習筆記啪兔

    由於 pureMVC Standard and MultiCore 兩個版本寫法幾乎沒啥差別,開發的專案都統一使用彈性比較大的 MultiCore 版本來實作。跟 pureMVC 相處了一陣子後,漸漸地對它也沒有什麼特別的不爽...快,所以 Cairngorm and pureMVC 的選擇,也就由 pureMVC 勝出了(當然是有惡勢力的...冏)。

    以下的範例還是使用 pureMVC Standard 來實作喔!這個範例應該能更容易理解 Mediator, Command and Proxy 合作的模式。


    1. 由 DataProxy 開始寫
    這次的 DataProxy 負責的是將 input textfield 中的文字儲存起來也提供清除的方法。 DataProxy 並不認識任何 Mediator。
    package com.mvc.model
    {
    import mx.collections.ArrayCollection;
    import org.puremvc.as3.patterns.proxy.Proxy;

    public class DataProxy extends Proxy
    {
    public static const NAME:String = 'dataProxy';
    public static const DATA_UPDATED:String ="data_updated";
    public static const SAVE_DATA:String = 'save_data';
    public static const CLEAR_DATA:String = 'clear_data';
    public function DataProxy()
    {
    super(NAME, new ArrayCollection);
    }
    public function save(obj:Object):void{
    list.addItem(list.length+" : "+obj);
    sendNotification( DATA_UPDATED, data );
    }
    public function clear():void{
    setData(new ArrayCollection);
    sendNotification( DATA_UPDATED, data );
    }
    //隱藏的 getter 給自己看的,順便轉換資料型態
    private function get list() : ArrayCollection {
    return data as ArrayCollection;
    }
    }
    }

    sendNotification( DATA_UPDATED, data );
    重點在這邊,DataProxy 只負責發送 data updated Notification 出去,其他啥都不管。

    2. UpdateDataCommand:
    用來處理呼叫 DataProxy save and clear function 用 command。可以避免由 view component 的 Mdeiator 直接取用修改 DataProxy。
    package com.mvc.control
    {
    import com.mvc.model.DataProxy;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;

    public class UpdateDataCommand extends SimpleCommand
    {
    public function UpdateDataCommand()
    {
    super();
    }
    override public function execute(notification:INotification):void
    {
    var proxy:DataProxy = facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
    if(notification.getName() == DataProxy.SAVE_DATA){
    proxy.save(notification.getBody());
    }else{
    // notification.getName() == DataProxy.CLEAR_DATA
    proxy.clear();
    }
    }
    }
    }


    資料處理完後接下來要準備製作 view component 囉!
    3. TestpureMVC2.mxml
    只負責發兩個 event "save" and "clear",順便提供兩個屬性給外面使用。
    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute" applicationComplete="facade.startup( this );" width="500" height="300">
    <mx:Metadata>
    [Event("save")]
    [Event("clear")]
    </mx:Metadata>
    <mx:Script>
    <![CDATA[
    import mx.collections.ArrayCollection;
    import com.mvc.ApplicationFacade;
    private var facade:ApplicationFacade = ApplicationFacade.getInstance();

    [Bindable]
    public var list:ArrayCollection;

    [Bindable]
    public var input:String;

    ]]>
    </mx:Script>
    <mx:Label text="pureMVC 練習啪兔" fontSize="16" />
    <mx:HBox width="100%" height="100%" y="30"
    paddingLeft="20" paddingRight="20" paddingBottom="20">
    <mx:VBox width="100%" height="100%">
    <mx:Label text="Input text" />
    <mx:TextInput id="inputTxt" width="100%" height="100%" text="Test pureMVC part2"/>
    <mx:Button label="Save"
    click="input=inputTxt.text;
    dispatchEvent(new Event('save'))" />
    </mx:VBox>
    <mx:VBox width="100%" height="100%">
    <mx:Label text="Result list" />
    <mx:List dataProvider="{list}" width="100%" height="100%" />
    <mx:Button label="Clear" click="dispatchEvent(new Event('clear'))" />
    </mx:VBox>
    </mx:HBox>
    </mx:Application>


    4. View component 做完就開始寫它的邏輯中介者 AppMediator
    package com.mvc.view
    {
    import com.mvc.model.DataProxy;
    import flash.events.Event;
    import mx.collections.ArrayCollection;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;

    public class AppMediator extends Mediator
    {
    public function AppMediator(mediatorName:String=null, viewComponent:Object=null)
    {
    super(mediatorName, viewComponent);
    //接收UI的Event
    app.addEventListener( "save", doClick);
    app.addEventListener( "clear", doClick);
    }
    override public function listNotificationInterests():Array
    {
    //要處理啥訊息都在這邊列好
    return [
    DataProxy.DATA_UPDATED
    ];
    }
    override public function handleNotification(notification:INotification):void
    {
    //處理 data updated notification,將收到的資料送回 app.list
    switch(notification.getName()){
    case DataProxy.DATA_UPDATED:
    app.list = notification.getBody() as ArrayCollection;
    }
    }
    //給自己看的,重點還是轉換物件型態
    private function get app():TestpureMVC2 {
    return viewComponent as TestpureMVC2;
    }
    //當收到 click event 只將 Notification發送出去
    private function doClick(event:Event):void{
    switch(event.type){
    case "save":
    sendNotification( DataProxy.SAVE_DATA, app.input);
    break;
    case "clear":
    sendNotification(DataProxy.CLEAR_DATA);
    break;
    }
    }
    }
    }

    這個 AppMediator 透過 DataProxy.SAVE_DATA and DataProxy.CLEAR_DATA Notification 跟外面溝通,如果把 SAVE and CLEAR 的常數建在別的 Class 中,你會發現 AppMediator 跟 DataProxy 一點關係都沒有,名言就是…我不認識你,你最好也不要認識我啊...XD

    5. 幾乎完成啦!接下來將 ApplicationFacade 寫好:
    package com.mvc
    {
    import com.mvc.control.StartupCommand;
    import mx.core.Application;
    import org.puremvc.as3.interfaces.IFacade;
    import org.puremvc.as3.patterns.facade.Facade;

    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:Application ) : void
    {
    sendNotification( STARTUP, app );
    }
    }
    }

    @@ 完全沒動過的標準寫法....

    6. 最後的 StartupCommand:
    package com.mvc.control
    {
    import com.mvc.model.DataProxy;
    import com.mvc.view.AppMediator;
    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{
    facade.registerMediator( new AppMediator(null, notification.getBody()) );
    facade.registerProxy( new DataProxy());
    //註冊 Save and Clear 對應的 UpdateDataCommand
    facade.registerCommand( DataProxy.SAVE_DATA , UpdateDataCommand );
    facade.registerCommand( DataProxy.CLEAR_DATA , UpdateDataCommand );
    }
    }
    }

    將 DataProxy.SAVE_DATA and DataProxy.CLEAR_DATA 指定到 UpdateDataCommand。
    完工!!
    這次練習的重點就是讓 Mediator and Proxy 關係更不好,當你可以弄到它們互相裝做不認識對方的時候,就是你成功的日子啊!

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

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