前回の続きです。
Robotlegs2はMessageDispatcherという機能があるのですが、探しても記事が無いので自分で試してみました。
#MessageDispatcherとは?
MessageDispatcherは登録したエンドポイントへString型のメッセージを送る機能です。
Spring Frameworkにありますね。
使い方はEventに似ています。
Eventと違うのは、より不特定多数に同時に通知させるのに適しています。
#実装サンプル
「クリックしてください」のテキストをクリックすると「変更しました」と表示する単純なアプリです。
今回のサンプルはここからダウンロードできます。
※サンプルはMVCモデルで実装しています。
※FlashDevelopを使用しています。
#サンプルコードの説明
前回のサンプルコードに少し手を加えました。
変更を入れていないソースは説明を省略します。
Config
/**
* メイン画面のコンフィギュレーションを行うクラス
*
* このクラスで以下の設定を実行しています。
* ・UserModel はシングルトンである
* ・UserView の対になるメディエータは UserViewMediator である
* ・メディエーターでUserChangeEvent.USER_CHANGEが呼ばれた場合、UserCommondを実行する
*
* @author ogino
*/
public class MainConfig implements IConfig
{
//-----------------------------------------------------
//コンポーネント
//-----------------------------------------------------
/** 画面 */
private var userView:UserView = new UserView();
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
[Inject]
/** インジェクター */
public var injector:IInjector;
[Inject]
/** メディエーターマップ */
public var mediatorMap:IMediatorMap;
[Inject]
/** イベントコマンドマップ */
public var commandMap:IEventCommandMap;
[Inject]
/** コンテキストビュー */
public var contextView:ContextView;
//-----------------------------------------------------
//メソッド
//-----------------------------------------------------
/**
* コンフィギュレーション実行
*/
public function configure():void
{
// おまじない
// MessageDispatcherはシングルトンである
injector.map(MessageDispatcher).asSingleton();
// UserModel はシングルトンである
injector.map(UserModel, "modelName").asSingleton();
injector.map(UserModel).asSingleton();
// UserView の対になるメディエータは UserViewMediator である
mediatorMap.map(UserView).toMediator(UserViewMediator);
// メディエーターでUserChangeEvent.USER_CHANGEが呼ばれた場合、UserCommondを実行する
commandMap.map(UserChangeEvent.USER_CHANGE).toCommand(UserCommand);
// このような形で値を注入することも可能
//var user:UserModel = new UserModel();
//injector.map(UserModel, "modelName").toValue(user);
// おまじないを設定すると、下記の処理が出来る
// 画面を切り替える このプロジェクトは一つしか画面がないので、このメソッドで初期化処理を実行
contextView.view.addChild(userView);
// DIコンテナからモデルを取得
var m:UserModel = injector.getInstance(UserModel, "modelName");
m.name = "テスト表示";
}
}
Configクラスでrobotlegs.bender.framework.impl.MessageDispatcherをシングルトン登録します。
injector.map(MessageDispatcher).asSingleton();
##Message
MessageDispatcherからMessageを受け取れるようにクラスを作成します。
まずMessageの基礎クラスを作成します。
この基礎クラスはEventクラスみたいなものです。
/**
* MessageDispatcherによってやり取りされるメッセージの基本クラスです。
*
* @author ogino
*/
public class Message
{
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
/** メッセージタイプ */
public var type:String;
//-----------------------------------------------------
//コンストラクタ
//-----------------------------------------------------
/**
* コンストラクタです。
*
* @param type メッセージタイプ
*/
public function Message(type:String)
{
this.type = type;
}
//-----------------------------------------------------
//メソッド
//-----------------------------------------------------
/**
* オブジェクトのストリング表現を返します。
*
* @return オブジェクトのストリング表現
*/
public function toString():String
{
var qualifiedClassName:String = getQualifiedClassName(this);
var className:String = qualifiedClassName.split("::")[1];
return "[" + className + " (type = " + type + ")]";
}
}
次に以下のようにMessageを継承したクラスを作成します。
/**
* ユーザー関連のメッセージクラス
*
* @author ogino
*/
public class UserMessage extends Message
{
//-----------------------------------------------------
//クラス定数
//-----------------------------------------------------
/** ユーザーデータ更新 */
public static const UPDATE_USER_DATA:String = "updateUserData";
//-----------------------------------------------------
//コンストラクタ
//-----------------------------------------------------
/**
* コンストラクタ
*
* @param type String
*/
public function UserMessage(type:String)
{
super(type);
}
}
カスタムイベントクラスを作るようなイメージです。
これでMessageDispatcherを使用する準備が整いました。
##Mediator
/**
* ユーザー画面のメディエータクラス
* UserModelとUserViewの仲介を行う
* 「ユーザの名前が変更された」というきっかけで「ビューにその名前を反映する」タスクを実行するだけのクラス
*
* @author ogino
*/
public class UserViewMediator extends Mediator
{
//-----------------------------------------------------
//コンポーネント
//-----------------------------------------------------
[Inject]
/** ユーザ画面 */
public var view:UserView;
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
[Inject(name="modelName")]
/** ユーザモデル */
public var user:UserModel;
[Inject]
/** メッセージディスパッチャー */
public var messageDispatcer:MessageDispatcher;
//-----------------------------------------------------
//オーバーライドしたメソッド
//-----------------------------------------------------
/**
* @inheritDoc
*/
public override function initialize():void
{
// 名前が変更されたときに呼び出すメソッドをセット
view.addEventListener(UserChangeEvent.USER_CHANGE, view_userChange);
// メッセージディスパッチャーにUserMessage.UPDATE_USER_DATAを受け取ったらコールバックを実行するようにセット
messageDispatcer.addMessageHandler(UserMessage.UPDATE_USER_DATA, UserMessageHandler);
}
/**
* @inheritDoc
*/
public override function destroy():void
{
// 登録したメソッドを削除
view.removeEventListener(UserChangeEvent.USER_CHANGE, view_userChange);
// メッセージディスパッチャーから受け取るメッセージを削除する。これでUserMessage.UPDATE_USER_DATAは受信できなくなる
messageDispatcer.removeMessageHandler(UserMessage.UPDATE_USER_DATA, UserMessageHandler);
}
//-----------------------------------------------------
//メッセージハンドラー
//-----------------------------------------------------
/**
* データ更新関連メッセージ メッセージハンドラー
*
* @param message
*/
protected function UserMessageHandler(message:String):void
{
switch (message)
{
case UserMessage.UPDATE_USER_DATA: // このメッセージを受け取ったら画面更新する
view.nameChange(user.name);
break;
default:
break;
}
}
//-----------------------------------------------------
//イベントハンドラー
//-----------------------------------------------------
/**
* 画面変更通知イベントハンドラー
*
* @param e
*/
private function view_userChange(e:UserChangeEvent):void
{
// イベントをクローンする
var newEvent:UserChangeEvent = UserChangeEvent(e.clone());
// フレームワークに通知 このイベントに紐付けられたCommandクラスが呼ばれる
eventDispatcher.dispatchEvent(newEvent);
}
}
MessageDispatcherをRobotlegs側から注入します。
[Inject]
public var messageDispatcer:MessageDispatcher;
次にMessageDispatcherから特定のメッセージを受信した時に、コールバック関数を呼び出すように設定します。
messageDispatcer.addMessageHandler(UserMessage.UPDATE_USER_DATA, UserMessageHandler);
特定のメッセージから受信したコールバック関数を削除するには、以下の通りに記述します。
messageDispatcer.removeMessageHandler(UserMessage.UPDATE_USER_DATA, UserMessageHandler);
addEventListener、removeEventListenerと同じ使い方が出来ます。
コールバック関数は必ずString型の引数を一つ設定します。
protected function UserMessageHandler(message:String):void
{
switch (message)
{
case UserMessage.UPDATE_USER_DATA:
view.nameChange(user.name);
break;
default:
break;
}
}
受け取ったメッセージごとにswitch文で処理を切り分けています。
このメソッドはUserMessage.UPDATE_USER_DATAを受信したら画面を更新しています。
##Command
/**
* ユーザーに関するコマンド実行
* このクラスはコンフィグで設定したイベントがメディエーターで呼ばれた場合、executeメソッドがフレームワーク側から実行される
* 他のクラスから、コマンドを直接実行させるのは控えること
*
* @author ogino
*/
public class UserCommand extends Command
{
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
[Inject]
/** イベント コマンドを実行したいイベントクラスをpublicで必ず記載すること */
public var event:UserChangeEvent;
[Inject(name="modelName")]
/** ユーザモデル */
public var user:UserModel;
[Inject]
/** メッセージディスパッチャー */
public var messageDispatcer:MessageDispatcher;
//-----------------------------------------------------
//オーバーライドしたメソッド
//-----------------------------------------------------
/**
* @inheritDoc
*/
override public function execute():void
{
user.name = "変更しました";
// メッセージディスパッチャーにメッセージを通知
messageDispatcer.dispatchMessage(UserMessage.UPDATE_USER_DATA);
}
}
MessageDispatcherをRobotlegs側から注入します。
[Inject]
public var messageDispatcer:MessageDispatcher;
モデルを更新したらMessageDispatcherにMessageを通知します。
messageDispatcer.dispatchMessage(UserMessage.UPDATE_USER_DATA);
これで、MessageDispatcherからUserViewMediator.UserMessageHandlerが呼ばれます。
UserViewMediator.UserMessageHandlerが呼ばれることにより、画面に表示されている文言が変更されます。
##Model
/**
* ユーザー名のモデルクラス
*
* @author ogino
*/
public class UserModel
{
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
//---------------
//name
//---------------
/**
* @private
*/
private var _name:String;
/**
* ユーザー名
*/
public function get name():String { return _name; }
/**
* @private
*/
public function set name(value:String):void
{
_name = value;
}
}
地味ですがMessageDispatcherでMediatorに更新を通知できるようになったので、EventDispatcherが必要なくなりました。
以上、サンプルコードの説明でした。
MessageDispatcherを利用するとより柔軟なDIコンテナの開発が出来そうです。
あくまでも使用例の一つなので他にもこういう使い方ができるよ、というのがあれば教えてください。