ActionScript
flash
mvc
ActionScript3
Robotlegs
More than 3 years have passed since last update.

tosikさんが投稿した記事を参考に、Robotlges2を使用したサンプルアプリケーションを作成しました。

Robotlegs2のGitHubはコチラ


Robotlegs2とは

FlashでDIコンテナを実装するためのMVCS(Model-View-Controller-Service)フレームワークライブラリです。


Robotlegsの利点

DIコンテナのフレームワークはいくつかありますが、RobotlegsがDIコンテナを実装する上で必要最低限の機能をそろえており、他のライブラリに比べて一番学習コストが低いです。

DIコンテナをとりあえず試用したい、勉強したい場合にオススメです。

※新卒2人に簡単にRobotlegs2の概要と実装方法を教えた後、約2週間ほどでライブラリを使いこなしていました。


実装サンプル

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

クリック前.png

クリック後.png

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

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

FlashDevelopを使用しています。


サンプルコードの説明


Main


Main.as

    /**

* フレームワーク 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


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
{
// おまじない

// 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();



name指定

injector.map(UserModel, "modelName").asSingleton();


また、以下の方法でもシングルトンになります。


toValue

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


UserViewMediator.as

    /**

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


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 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は通常通りの実装です。


UserModel.as

    /**

* ユーザー名のモデルクラス
*
* @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));
}
}



UserView.as

    /**

* ユーザー情報表示画面
*
* @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));
}



UserChangeEvent.as

    /**

* ユーザーがデーターを変更した時のイベントハンドラー
*
* @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 の実装したサンプルコードの説明になります。