コンポーネント指向モデルSCA

PACのようにコンポーネント指向なモデルとしてService Component Architecture英語Wikipedia(日本版はほとんど書いてない)というのを知った。
SOAの文脈で読んだが、その基本モデル(この回の最後の図)再帰的モデルの図(この回の図)を見ると使えないかと思った。
ITproでの連載記事
マイコミジャーナルの記事
SCA自体はその実行環境が必要そうだが、仕様からなにかアイディアを得られないか調べてみたい。

PACの初出は1987年*1、SACの2005年11月に0.9、2007年3月に1.0のバージョンがリリースということでかなり新しいし、並居る大企業やプロジェクト(Apacheとか)が協賛しているので、かなり洗練されたモデルではないかという期待もある。

呼び方の変更

PACということでPresentation、Control、Abstractionという言葉を使ってきたが、いざ実装するとControl以外のが長ったらしい。
そこでPresentation → View、Abstraction → Model と呼ぶ&命名規則に使うことにした。MVCと同じにする(ControlはPACのままだが)ので、ますますHMVCに見えてくるが

MXMLコンポーネントをActionscriptで扱う

今回のパターンでは、PresentationをControlが操作する(ただ既述のように基本的に生成時だけ)。
PresentaionはせっかくFlexだからMXMLで定義する。つまりPresentationオブジェクトはMXMLコンポーネントになる(MXMLモジュールは分からないので除外する)。対してControlはActionscriptで記述されたクラスで表す。


すると「ControlがPresentationを操作する」には、ActionscriptMXMLコンポーネントを扱えないといけない。
通常は、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

こうして実装面も考えると、各部分の役割は以下のようになるかと考えた。

Presentation

  • UI
  • ユーザー入力を内部イベントの送出によってControlに知らせる
    • イベントによるため、Controlへの依存はない

Control

  • 他エージェントと連携するのに必要な機能を司る、つまり、
    • 外部から参照する自エージェントの状態保持(byクラス変数)
    • 内部のイベントを拾った時に自エージェントの状態を判断する(byプライベートメソッドのイベントハンドラ
    • 外部へのイベント送出
  • 自エージェント内のコントロール
    • Presentaion, Abstractionのインスタンス生成
    • 内部イベントや外部からのリクエストに応じてAbstractionにデータ処理をさせたりPresentationの見た目変更をさせたりする。

Abstraction

  • エージェント内の機能のロジックとデータを司る
  • 内部処理のイベント(処理の終了ぐらいしか思い当たらない)をイベントによってControlに知らせる
    • Presentationと同じくContolへの依存はない


次はもっと実装レベルについて。

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はそれをキャッチすることにする。

Flex/AIRでPAC的に実装するトライアル(1)

PACでFlex/AIRアプリを作るとき実際どのような構成・実装になるか試してみることにした。

このサイトにPureMVCというFlexフレームワークの詳しい説明がある。これはMVCの実装を考える時に役立つ。
ただM/V/Cにあたるクラス以外にもCommandパターンやMeditatorパターン用のクラスも出てきて、自分のアプリではそこまでいらないな、という感じ。

個々のエージェントの実装は、なるべくPACのP/A/Cにあたる3クラスだけですませたい。←そもそも自分のアプリでCairngormを使いたくない理由がこれ。