Robotlegs2のMessageDispatcherを試してみる

  • 1
    Like
  • 0
    Comment
More than 1 year has 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コンテナの開発が出来そうです。
あくまでも使用例の一つなので他にもこういう使い方ができるよ、というのがあれば教えてください。