まえにDoTweenerとかを拡張して使うアイディアをかきましたが、今度はCommandListを拡張してみます。

CommandList自体は抽象クラス(的)なので、実際にはCommandListを継承しているSerialListかParallelListをさらに拡張する方法を取ります。

次のようにAPIに通信してXMLデータを取得、そのXMLをパースし、モデルクラスを用意するような一連の処理を実装します。

APIに渡す値などはコンストラクタの引数として外部から受け取り、コンストラクタ内で任意のCommandを生成してsuperまたはaddCommandにセットしておきます。

通信後に整形した情報などは latestDataなどに突っ込んでおいて、あとの処理やエラーハンドリングは、このクラスを利用するほうに任せます。

package com.kayac.commands {
    import flash.events.*;
    import flash.net.*;
   
    import jp.progression.casts.*;
    import jp.progression.commands.*;
    import jp.progression.core.commands.Command;
    import jp.progression.events.*;
    import jp.progression.loader.*;
    import jp.progression.*;
    import jp.progression.scenes.*;
  
    import com.kayac.model.UserInfo;
   
    public class GetUserInfo extends SerialList {
        private static const API:String = "/api/user/info";
        private var _userId:String;
      
        public function GetUserInfo( userId:String, initObject:Object = null ) {
            super( initObject );
          
            _userId= userId;
          
            var request:URLRequest = new URLRequest( API );
            request.data = new URLVariables();
            request.data.user_id = _userId;
          
            addCommand(
                new LoadURL( request ),
                new Func( function():void {
                    var xmlData:XML = new XML( this.latestData );
                    if( xmlData.error == "1" ) throw new Error(xmlData.message);
                   
                    var data:UserInfo = new UserInfo();
                    data.parseXML(xmlData);
                   
                    this.parent.latestData = data;
                } )
            );
          
        }
        public override function clone():Command {
            return new GetUserInfo( _userId, this );
        }
    }
}

利用するときはこんな感じ。

package {
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.text.TextField;
   
    import jp.progression.commands.*;
    import jp.progression.core.commands.Command;
   
    import com.kayac.commands.GetUserInfo;
    import com.kayac.model.UserInfo;
   
    public class DocumentClass extends Sprite {
        public function DocumentClass () {
           
            var loading:MovieClip = new LoadingCycle();
            var nameField:TextField = new TextField();
            var sWidth:uint = stage.stageWidth;
            var sHeight:uint = stage.stageHeight;
           
            var userId:String = "doke";
           
            var com:Command = new SerialList( null,
                new Prop( loading, { x: sWidth*0.5, y:sHeight*0.5 } ),
                new AddChild( this, loading ),
                new GetUserInfo( userId ),
                new Func(
                    function():void {
                        var userInfo:UserInfo = this.latestData as UserInfo;
                        nameField.text = userInfo.name;
                    }
                ),
                new AddChild( this, nameField )
            );
           
            com.error( function( e:Error):void {
                trace( e.message )
                this.executeComplete();
            } );
           
            com.execute();
        }
    }
}

このようにインスタンス化できるようにすることで、コードを再利用可能だし、Commandなので他の処理との連携もバッチリです。

ちなみにエラーハンドリングは敢えてCommandの外に任せてますが、こうすることで場面の文脈に応じて処理を柔軟に実装できます。案件の規模によりますが、エラー時の表現処理を通信処理に依存させると使いにくい場合が多いです。

で すので、たとえば通信以外のXMLがおかしいとかシステムエラーなんかの不整合が起こったときも、GetUserInfo的には例外をスローしておいて、 後はコンテキストに応じてエラー処理させることで、役割を分担させることができます。(errorメソッド内の関数のスコープはErrorが発生した Commandになるので、中断するか無視するかなども、状況に応じて選択できます)

もし内容が空だったらダミーデータ突っ込むとか、リトライするみたいなのであればCommand内でフォローアップしてもいいかもしれません。

HTML5飯