コンポーネント指向モデルSCA
PACのようにコンポーネント指向なモデルとしてService Component Architecture英語Wikipedia(日本版はほとんど書いてない)というのを知った。
SOAの文脈で読んだが、その基本モデル(この回の最後の図)、再帰的モデルの図(この回の図)を見ると使えないかと思った。
ITproでの連載記事
マイコミジャーナルの記事
SCA自体はその実行環境が必要そうだが、仕様からなにかアイディアを得られないか調べてみたい。
PACの初出は1987年*1、SACの2005年11月に0.9、2007年3月に1.0のバージョンがリリースということでかなり新しいし、並居る大企業やプロジェクト(Apacheとか)が協賛しているので、かなり洗練されたモデルではないかという期待もある。
MXMLコンポーネントをActionscriptで扱う
今回のパターンでは、PresentationをControlが操作する(ただ既述のように基本的に生成時だけ)。
PresentaionはせっかくFlexだからMXMLで定義する。つまりPresentationオブジェクトはMXMLコンポーネントになる(MXMLモジュールは分からないので除外する)。対してControlはActionscriptで記述されたクラスで表す。
すると「ControlがPresentationを操作する」には、ActionscriptでMXMLコンポーネントを扱えないといけない。
通常は、MXML内でActionscriptクラスのインスタンスを生成する(Scriptでnewしたり、タグで埋め込むことで(リンク先ではUIComponentを拡張しているがその必要はない。ただFlexBuilderのデザイン画面でControlが使えるようにはなる))。そしてASクラスのインスタンスにMXML側からメッセージを送る。
しかしActionscript側からだけでMXMLコンポーネントを扱う必要がある。違う言語で表されたものをどうやって?
Adobeの資料にもMXML側からActionScriptで作られたクラス/コードを利用する例しかない。
実はMXMLはコンパイルされてファイル名と同名のASクラスに変換されている。(これはコンパイラに-keepオプションを指定するとgeneratedという中間生成されたファイルが残り、そこを見ると分かる)
そのためView.mxmlを利用するには以下のようにすればいい。
var view:View = new View();
もしView.mxmlで
とコンポーネントを使ってれば、(Contorolにあたる)AS側では
view.dgrid.dataProvider = {col1:"a", col2:"b"};
というようにアクセスできる。
ただし、viweが他のContainerにaddChildされるまではコンポーネントは初期化されてないため、上のコードをaddChildの前に実行するとdgrid==nullでエラーになるのに注意。
ただしFlex Builderで「MXMLアプリケーション」を指定するとMXMLファイルがルートになる。そのためContorol側でPresentationのインスタンスを作るわけにもいかない(もちろん自動生成されたMXMLファイルを使わないという手でもいいが)。
その時はControlのAS内で、ApplicationクラスからPresentationのインスタンスを取得する方法がある。
var view:View = Application.application as View;
コンパイルのオプションに-keepを指定すると-keepオプションはid:secondlifeさんのとこで知った。bindableのすばらしい解説。
http://d.hatena.ne.jp/secondlife/20070326/1174904664
URLLoaderタイムアウトに対処する
ここしばらくネットにまともにつながらない。
そのせいでFlexアプリでURLLoader#load()でしょっちゅうタイムアウトする。その時ストリームエラーが投げられてくる。
このエラーは非同期で起きるのでtry/catchでは対処できない。イベントリスナを使うことになる。
loader.addEventListener(IOErrorEvent.IO_ERROR, catchIOError);
function catchIOError(event:IOErrorEvent){
エラー処理
}
元:Matt Maher - Personal Training, Nutrition Coaching
タイムアウトを考えるならURLLoaderWithTimeoutを使わせてもらおう。タイムアウト時間を指定できる。
http://svn.rails2u.com/as3rails2u/trunk/src/com/rails2u/net/URLLoaderWithTimeout.as
ただこれを使っても、親クラスのURLLoaderのIOErrorイベントは投げられる。つまり、URLLoaderWithTimeout#loadWithTimeout()では、このメソッドで指定した時間後のタイムアウトとデフォルトのタイムアウトの両方でIOErrorイベントが起きるので注意。例えばIOError用のハンドラの中でremoveEventListenerをすると2回目のイベントがハンドルされない。
P/A/Cの役割revised
PACをなるべく簡単に実装したい
ここで、自分的にPACのそれぞれの役割をまとめてみる。
Presentation
- UI
- ユーザーの入力を(必要であれば)Controlに渡す
Control
- 他エージェントと連携するのに必要な機能を司る、つまり、
- 外部から参照する自エージェントの状態保持
- 上位エージェントへのメッセージング
- 自エージェント内でのPresentationとModelの仲介
Abstraction
- エージェント内の主な機能のロジックとデータを司る
ここから、各部分間の関係について実装面から考えてみる。
P(Presentation)-A(Abstraction)
AのデータをPに反映させるのが唯一求められること。こういうのはよくObserverパターンが用いられる。
ASではデータバインド機構があるのでそれを使わない手はない。
しかしPACでは、PとAの間のやり取りは認められてない。だがせっかくバインド機構があるのに手間はかけたくない。
ということでPACから外れて、やっぱり上のようにする。
こうなると本当はPACよりHMVCというので紹介されたパターンになる。
http://www.javaworld.com/jw-07-2000/jw-0721-hmvc.html
HMVCはHierarchical-MVC(階層MVC)のことで、MVCをPAC化したようなものになっている。
この記事は詳しく書かれていてタメになると思う。
A-C(Control)
AのデータはバインディングでPへ渡すとしたので、AからCへはMVCのM-Cと同じく基本的に何もすることはない。ただし他エージェントとのやりとりはCでするため、例えばAでの処理の終了が外部の動きにも関係する場合は、処理終了をCに知らせる必要があるだろう。これは(P-Cと同じく)イベントの送出で実装した方が依存しなくていいだろう。
CからAは、Cで何かイベントを感知した時(Pからのイベントや外部からの呼び出し)に、Aを操作できればいい。これは普通にCからA(のインスタンス)のメソッドを呼ぶことでいいだろう。
P-C
PからCへはユーザーからの入力を伝達する。(そして必要に応じて外部やAに伝達される。Pから-Aに直接ユーザー入力を教えてもいいが、外部エージェントに関係する入力もあるだろうから、Cを経由するルールにする)
CからPはMVCのVCと同じく何もすることなし。だが、PACではCが全ての起点になるため、実際にはControlオブジェクトからPresentationインスタンスを生成しないといけない(Abstractionインスタンスも同様)。
よって、なるべく依存性をなくす形を取るために、Controlオブジェクト内にPresentationインスタンスを持つのは仕方ないとして(DIとかFactory用に余計なクラスを作らない)、Presentationからはイベントを送出してControlはそれをキャッチすることにする。