ActionScript
flash
mvc
ActionScript3
Robotlegs

Robotlegs2のMessageDispatcherを試してみる

More than 3 years have passed since last update.

前回の続きです。

Robotlegs2はMessageDispatcherという機能があるのですが、探しても記事が無いので自分で試してみました。


MessageDispatcherとは?

MessageDispatcherは登録したエンドポイントへString型のメッセージを送る機能です。

Spring Frameworkにありますね。

使い方はEventに似ています。

Eventと違うのは、より不特定多数に同時に通知させるのに適しています。


実装サンプル

「クリックしてください」のテキストをクリックすると「変更しました」と表示する単純なアプリです。

1.png

2.png

今回のサンプルはここからダウンロードできます。

※サンプルはMVCモデルで実装しています。

FlashDevelopを使用しています。


サンプルコードの説明

前回のサンプルコードに少し手を加えました。

変更を入れていないソースは説明を省略します。


Config


MainConfig.as

    /**

* メイン画面のコンフィギュレーションを行うクラス
*
* このクラスで以下の設定を実行しています。
* ・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クラスみたいなものです。


Message.as

    /**

* 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を継承したクラスを作成します。


UserMessage.as

    /**

* ユーザー関連のメッセージクラス
*
* @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


UserViewMediator.as

    /**

* ユーザー画面のメディエータクラス
* 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


UserCommand.as

    /**

* ユーザーに関するコマンド実行
* このクラスはコンフィグで設定したイベントがメディエーターで呼ばれた場合、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


UserModel.as

    /**

* ユーザー名のモデルクラス
*
* @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コンテナの開発が出来そうです。

あくまでも使用例の一つなので他にもこういう使い方ができるよ、というのがあれば教えてください。