イベントによるオブジェクト間のコミュニケーション

さて前回の続き、というか今回のモデルの実装法の続き。

今度はイベントを使ったオブジェクト間のやり取りについて。View(Presentation改め)とControl、Controlと別エージェントのControlは疎結合のために原則イベントで協調するようにする。
ModelとControl間は原則Controlからしか働きかけないので含めてないが、必要な場合はView-COntrolと同じになる。

ActionScript3ではEventDispatcherクラスがイベントの送出/受信を行うので、必然的にControlはEventDispatcherを継承することになる。(ただしこれはViewのためではない。ControlはViewに対する参照を持っている(持たざるを得ない)のでイベントを使う必要はない参照)。

一方、Flex/FlashのビジュアルオブジェクトはDisplayObjectを継承している(これがEventDispatcherを継承している)ので、Viewは気にしなくてもイベントが扱える。


ということで仕組みとしては、

  • ViewとControl:Viewからイベントを送出して、Controlでイベントを受け、イベントの種類によってControlが処理をする
  • Control(子)とControl(親):子からイベントを送出して、親でイベントを受け、イベントの種類によって親が処理をする。

しつこいが、ControlからView、親Controlから子Controlは親側が参照を持つのでイベントは使わない。


そして、この仕組みを実装しようとしたら(以下View->Controlについてのみ書く。Control->Controlも同じ)、次のような感じになると思う。

View





Control

public class Control extends EventDispatcher {
var view:View;
public function Control() {
this.addEventListener("イベントA",handlerA);
}
 ・・・
}


しかし、こうはできない。Viewの方はいいが、ControlでこのようにaddEventListenerしてもイベントAが受け取れないのである。


なぜかというと、Viewで送出されたイベントはViewにしか聞こえないからだ。
AS3のイベントを調べると、必ずイベントフローについて知るはずだ。特にこのサイトが分かりやすい。ビジュアルなので一発理解。
http://void.heteml.jp/blog/archives/2006/07/as3_eventflow2.html


それを知ると、親オブジェクトにもイベントがバブリングして(一方向であれ往復であれ)届くと思ってしまう。ただ注意しないといけないのは、それは親がDisplayObjectの場合だけ、つまり親にaddChild()された子DisplayObjectのイベントだけが親へそしてStageへと届くのだ。

だからViewとControlの場合、ControlがViewインスタンスを持っているとしてもViewのイベントはControlまで来ない。
イメージ的にはこんな感じ。

・・・当たり前なのだろうか?


ともあれこれを踏まえてControlのコードは以下のようになる。

public class Control extends EventDispatcher {
private var view:View = new View;
public function Control() {
view.addEventListener("イベントA",handlerA);
}
 ・・・
}

さらにViewのコードもControlに埋め込んでしまえる。

public class Control extends EventDispatcher {
private var view:View = new View;
public function Control() {
view.addEventListener("イベントA",handlerA);
view.addEventListener(FlexEvent.CREATION_COMPLETE, function():void {
dispatchEvent(new Event("イベントA"));
});
}
 ・・・
}

まるで飼い主と飼い犬のような関係だ。飼い主は犬にお腹が空いたらイベントAと鳴けと教えておいて(これがaddEventListener)、犬がお腹が空いた(上の場合FlexEvent.CREATION_COMPLETEが起きた時)らイベントAと鳴く(dispatchEvent)。そうすると飼い主が何かしら(handleA)してくれる。犬にはそれしかできない。
できれば飼い主に食事を持ってくるよう直接飼い主にアクセスしたいが(飼い主#feedDog()みたいに)犬にはその飼い主がどこにいるか分からないから鳴くしかなく、それで何が起きるかも飼い主次第なのだ。しかも飼い主は特定の人物とはかぎらない。指示ができて何かしらの行動ができればいいのだ。


そこまでして(?)この方法で得るとこはあるのかというと、一重に疎結合のためだ。そのおかげで飼い主は誰でもなれる。
イベントドリブンと聞いて思い浮かぶ非同期処理だけなら別に疎結合でなくてもいいが、疎結合はつまり再利用性を高め(=継承クラスを作るのに便利とか)、カプセル性を高める(=そのクラスで行うことが明確になる=責務の分離)ことが利点なのかと思う。


他の方法でも(仲介パターンとか)いいかもしれないが、ActionScript3の標準機構を使うならイベント方式が一番いい気がする。あまり自信がないが、とりあえずイベント方式で行ってみよう。