tosikさんが投稿した記事を参考に、Robotlges2を使用したサンプルアプリケーションを作成しました。
Robotlegs2のGitHubはコチラ
Robotlegs2とは
FlashでDIコンテナを実装するためのMVCS(Model-View-Controller-Service)フレームワークライブラリです。
Robotlegsの利点
DIコンテナのフレームワークはいくつかありますが、RobotlegsがDIコンテナを実装する上で必要最低限の機能をそろえており、他のライブラリに比べて一番学習コストが低いです。
DIコンテナをとりあえず試用したい、勉強したい場合にオススメです。
※新卒2人に簡単にRobotlegs2の概要と実装方法を教えた後、約2週間ほどでライブラリを使いこなしていました。
実装サンプル
「テスト表示」のテキストをクリックすると「変更しました」と表示する単純なアプリです。
今回のサンプルはここからダウンロードできます。
※サンプルはMVCモデルで実装しています。
※FlashDevelopを使用しています。
サンプルコードの説明
Main
/**
* フレームワーク Robotlegs2 サンプルプロジェクト
* as3であればGraphics,Stage3D、Flexで使用可能
* このプロジェクトはgraphicsで記載
*
* @author ogino
*/
public class Main extends Sprite
{
//-----------------------------------------------------
//コンポーネント
//-----------------------------------------------------
/**
* コンストラクタ
*/
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
//-----------------------------------------------------
//イベントハンドラー
//-----------------------------------------------------
/**
* 初期化
*
* @param e Event
*/
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// Robotlegs2を起動するための設定
// MVCSBundleをセット
// コンフィグクラスをセット
// 最後にコンテキストビューをセットし、初期化処理を実行
var context:Context = new Context();
// おまじない
context.install(MVCSBundle);
// コンフィグクラスをセット
context.configure(MainConfig);
// コンテキストビューとしてこの画面をセット
context.configure(new ContextView(this));
// Robotlegs2初期化
context.initialize();
}
}
Robotlegs2を利用するにあたり、以下は必ず決まった書き方(おまじない)になります。
var context:Context = new Context();
context.install(MVCSBundle);
context.configure(MainConfig);
context.configure(new ContextView(this));
context.initialize();
MVCSを実現するだけなら、MVCSBundleをinstallすれば大丈夫です。
context.install(MVCSBundle);
ContextViewにドキュメントクラスを登録します。
context.configure(new ContextView(this));
ドキュメントクラスでなくても大丈夫ですが、変な動作するのも怖いのでinitialzeメソッドを呼び出すクラスを登録するのが無難です。
コンフィグクラスは連続で設定することも可能です。
context.configure(MainConfig).
configure(ServiceConfig).
configure(SocketServiceConfig);
最後にinitializeを呼び出します。これで初期化は終了です。
context.initialize();
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
{
// おまじない
// 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.api.IConfigをimplementsする必要があります。
public class MainConfig implements IConfig
Configクラスを実装する上で、以下のプロパティを設定する必要があります。
[Inject]
public var injector:IInjector;
[Inject]
public var mediatorMap:IMediatorMap;
[Inject]
public var commandMap:IEventCommandMap;
この3つはRobotlegs側が自動的に注入されるため、おまじないはありません。
※Injectタグをつけるプロパティは必ずpublicにしてください。
ドキュメント画面をConfigクラスで何か操作する場合、ContextViewをRobotlegsから注入する必要があります。
[Inject]
public var contextView:ContextView;
configureメソッド
configureメソッドでDIコンテナ化するクラスを登録します。
シングルトン登録は以下の書き方になります。
injector.map(UserModel).asSingleton();
injector.map(UserModel, "modelName").asSingleton();
また、以下の方法でもシングルトンになります。
var user:UserModel = new UserModel();
injector.map(UserModel, "modelName").toValue(user);
MediatorをViewに紐付けるには、以下の書き方になります。
mediatorMap.map(UserView).toMediator(UserViewMediator);
これでUserViewの対になるMediatorとして、UserViewMediatorがRobotlegsに登録されました。
※MediatorとViewは必ず1対1になる仕様です。
Commondを実行するイベントの登録は、以下の書き方になります。
commandMap.map(UserChangeEvent.USER_CHANGE).toCommand(UserCommand);
これでMediatorでUserChangeEvent.USER_CHANGEが呼ばれた場合、UserCommandクラスのメソッドが呼ばれるようになりました。
Mediator
/**
* ユーザー画面のメディエータクラス
* UserModelとUserViewの仲介を行う
* 「ユーザの名前が変更された」というきっかけで「ビューにその名前を反映する」タスクを実行するだけのクラス
*
* @author ogino
*/
public class UserViewMediator extends Mediator
{
//-----------------------------------------------------
//コンポーネント
//-----------------------------------------------------
[Inject]
/** ユーザ画面 */
public var view:UserView;
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
[Inject(name="modelName")]
/** ユーザモデル */
public var user:UserModel;
//-----------------------------------------------------
//オーバーライドしたメソッド
//-----------------------------------------------------
/**
* @inheritDoc
*/
public override function initialize():void
{
// 名前が変更されたときに呼び出すメソッドをセット
user.addEventListener(Event.CHANGE, changeView);
view.addEventListener(UserChangeEvent.USER_CHANGE, view_userChange);
}
/**
* @inheritDoc
*/
public override function destroy():void
{
// 登録したメソッドを削除
user.removeEventListener(Event.CHANGE, changeView);
}
//-----------------------------------------------------
//イベントハンドラー
//-----------------------------------------------------
/**
* 画面更新
*
* @param e Event
*/
public function changeView(e:Event):void
{
view.nameChange(user.name);
}
/**
* 画面変更通知イベントハンドラー
*
* @param e
*/
private function view_userChange(e:UserChangeEvent):void
{
// イベントをクローンする
var newEvent:UserChangeEvent = UserChangeEvent(e.clone());
// フレームワークに通知 このイベントに紐付けられたCommandクラスが呼ばれる
eventDispatcher.dispatchEvent(newEvent);
}
}
Mediatorは必ずrobotlegs.bender.bundles.mvcs.Medatorクラスを継承する必要があります。
public class UserViewMediator extends Mediator
Mediatorに紐付けられたView(この場合だとUserView)はMediator内でInjectタグで注入されます。
[Inject]
public var view:UserView;
Configクラスでシングルトン登録されたクラス(この場合はUserModelクラス)がある場合、Robotlegs側から注入されます
[Inject]
public var user:UserModel;
nameを指定すると、Configクラスでnameを指定することでRobotlegsから注入されます。
nameが違う場合、nameが無指定の場合は注入されません。
[Inject(name="modelName")]
public var user:UserModel;
MediatorクラスのeventDispatcherに、Configクラスで登録したイベントクラスをdispatchEventに渡すことで、Commandクラスを実行できます。
eventDispatcher.dispatchEvent(new UserChangeEvent(UserChangeEvent.USER_CHANGE));
※子画面からUserChangeEvent.USER_CHANGEをdispatchEventしてもCommandクラスは実行されないので注意
###オーバーライドしたメソッドの説明
1.initializeメソッド
Mediatorに紐付けられたViewがstageにaddChildされたタイミングでRobotlegs側から呼び出されます。
2.destroyメソッド
Mediatorに紐付けられたViewがstageにremoveChildされたタイミングでRobotlegs側から呼び出されます。
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 contextView:ContextView;
//-----------------------------------------------------
//オーバーライドしたメソッド
//-----------------------------------------------------
/**
* @inheritDoc
*/
override public function execute():void
{
var view:UserView = new UserView();
view.y = 20;
user.name = "変更しました";
// 画面を追加表示 コマンドクラスで画面遷移を実施するのがよさそう
contextView.view.addChild(view);
}
}
RobotlegsにおいてCommandクラスはControllerです。
注意
Commandクラスは使い捨てです。
Commandに紐付けられたイベントクラスがMediatorから呼ばれるたびに、Robotlegs内でnewされています。
Commandは必ずrobotlegs.bender.bundles.mvcs.Commandクラスを継承する必要があります。
public class UserCommand extends Command
Commandクラスでは、Configクラスで紐付けられたイベントクラス(この場合はUserChangeEventクラス)をInjectタグで指定すると、Robotlegs側から注入されます。
[Inject]
public var event:UserChangeEvent;
MVCSの場合、CommandクラスからServiceを呼び出します。
[Inject]
public var service:SocketService;
override public function execute():void
{
service.send();
}
###オーバーライドしたメソッドの説明
1.executeメソッド
Commandに紐付けられたイベントクラスがMediator.eventDispatcher.dispatchEventを実行することで、Robotlegs側から呼び出されます。
必ずoverrideしてください!
Model、View、Event
Model、View、Eventは通常通りの実装です。
/**
* ユーザー名のモデルクラス
*
* @author ogino
*/
public class UserModel extends EventDispatcher
{
//-----------------------------------------------------
//プロパティ
//-----------------------------------------------------
//---------------
//name
//---------------
/**
* @private
*/
private var _name:String;
/**
* ユーザー名
*/
public function get name():String { return _name; }
/**
* @private
*/
public function set name(value:String):void
{
_name = value;
// 変更を通知
dispatchEvent(new Event(Event.CHANGE));
}
}
/**
* ユーザー情報表示画面
*
* @author ogino
*/
public class UserView extends Sprite
{
//-----------------------------------------------------
//コンポーネント
//-----------------------------------------------------
/** ユーザー名表示領域 */
private var nameTextField:TextField;
//-----------------------------------------------------
//コンストラクタ
//-----------------------------------------------------
/**
* コンストラクタ
*/
public function UserView()
{
super();
nameTextField = new TextField();
nameTextField.addEventListener(MouseEvent.CLICK, nameTextFieldClickHandler);
addChild(nameTextField);
}
//-----------------------------------------------------
//メソッド
//-----------------------------------------------------
/**
* 名前変更
*
* @param name
*/
public function nameChange(name:String):void
{
nameTextField.text = name;
}
//-----------------------------------------------------
//イベントハンドラー
//-----------------------------------------------------
/**
* 名前テキストをクリックしたときのイベントハンドラー
*
* @param e
*/
private function nameTextFieldClickHandler(e:MouseEvent):void
{
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK);
dispatchEvent(e);
// メディエーターに更新イベントを通知
dispatchEvent(new UserChangeEvent(UserChangeEvent.USER_CHANGE));
}
/**
* ユーザーがデーターを変更した時のイベントハンドラー
*
* @author ogino
*/
public class UserChangeEvent extends Event
{
//-----------------------------------------------------
//イベント定数
//-----------------------------------------------------
/** ユーザー変更 */
public static const USER_CHANGE:String = "userChange";
//-----------------------------------------------------
//コンストラクタ
//-----------------------------------------------------
/**
* コンストラクタ
*
* @param type
* @param bubbles
* @param cancelable
*/
public function UserChangeEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
//-----------------------------------------------------
//オーバーライドしたメソッド
//-----------------------------------------------------
/**
* @inheritDoc
*/
override public function clone():Event
{
var e:UserChangeEvent = new UserChangeEvent(type, bubbles, cancelable);
return e;
}
}
以上、Robotlegs2 の実装したサンプルコードの説明になります。