• 11
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Robotlegs2 (RL2) はドキュメント不足と初代 Robotlegs のドキュメントが混在するため学習しづらいと感じ、僕なりに導入部分をまとめてみようと思います。

注)この記事では Robotlegs2 のことを単に Robotlegs と書いていることがあります。

http://www.robotlegs.org/

Robotlegs の動作環境

Robotlegs は以下の様な環境で動作します。

  • ActionScript3.0 を用いる
  • Adobe Flash
  • Flex
  • Adobe AIR
    • iOS/Android やデスクトップ環境で動く

Robotlegs の基本概念

Robotlegs は MVC フレームワークです。
特徴と言えるのは Dependency Injection (DI) を用いた制御の反転 (IoC) でしょう。

Dependency Injection (DI)

Robotlegs の DI のサンプルコードです。

Foo.as
public class Foo
{
    [Inject]
    public var bar:Bar;
}
Main.as
var injector:Injector = new Injector;

// map
injector.map(Bar).asSingleton();

// inject
var foo:Foo = new Foo;
injector.injectInto(foo);

trace(foo.bar is Bar); // => true

ここでは Bar クラスを「シングルトン」としてインジェクタに登録し、そのインジェクタで Bar クラスのインスタンスのインジェクトを行っています。
Bar のインスタンスはこのインジェクタを用いて他のインスタンスに「注入」されているのです。
インジェクトされる先のメンバは [Inject] と書かれたメンバの中で型(クラス・インターフェイス)が一致するものです。

IoC with DI

制御の反転 (IoC) とは、「タスクの実行のきっかけを作った人は、実行するタスクも知っている」という「作った人」から「タスク」への依存関係を切り離して「タスクは、何々をきっかけに実行する」と置き換える依存関係の反転を指す言葉です。
タスクにかぎらず色々な制御関係を反転することも IoC と言います。

Robotlegs の Injector の機能だけを利用した IoC で「ユーザ名が変更されたら表示する」という機能を実装してみましょう。

反転されていない書き方

User.as
public class User
{
    private var _printer:Printer = new Printer;
    private var _name:String;
    public function get name():String { return _name; }
    public function set name(value:String):void
    {
        _name = value;
        _printer.print("The user's name is " + name + ".");
    }
}
Main.as
var user:User = new User();
user.name = "Taro"; // => "The user's name is Taro."

一応表示されます。実装も短くてシンプルです。
しかし、これだと単なる User クラスが文字を表示する機能 Printer を知る必要がありますよね。
この依存関係はあまり良くないので、DI で切り離してみましょう。

制御の反転を行った書き方

User.as
public class User
{
    public const nameChanged:Signal = new Signal;
    private var _name:String;
    public function get name():String { return _name; }
    public function set name(value:String):void
    {
        _name = value;
        nameChanged.dispatch();
    }
}
PrintUsernameCommand.as
public class PrintUsernameCommand
{
    [Inject]
    public var user:User;

    private var _printer:Printer = new Printer;

    public function execute():void
    {
        _printer.print("The user's name is " + user.name + ".");
    }
}
Main.as
// configuration
var user:User = new User;
injector.map(User).toValue(user);

// when user.name changed then print the name
user.nameChanged.add(function():void
{
    // command instantiation
    var command:PrintUsernameCommand = new PrintUsernameCommand();
    injector.injectInto(command);

    // command execution
    command.execute();
});

user.name = "Taro"; // => "The user's name is Taro."

注) as3signals というライブラリを使っています http://qiita.com/tosik/items/2c1596abf3d43077fe6e

かなり複雑に見えるかもしれませんが、User クラスが Printer クラスを知らなくてよくなりました。
User と PrintUsernameCommand に分離し、User#name の変更時にその user インスタンスをインジェクとした PrintUsernameCommand の execute が実行されるようになっています。

ただ、依存分離が行えても Main 部分が雑だし記述量が多いのが面倒です。
そこで登場するのが Robotlegs です。(さっきのは Robotlegs の Injector しか使っていません)

Robotlegs による MVC の実例

先ほどの「ユーザ名が変更されたら表示する」を MVC で組み立てましょう。
今回は Printer を用いず TextField で名前を表示してみます。

Model

User.as
public class User
{
    public const nameChanged:Signal = new Signal;
    private var _name:String;
    public function get name():String { return _name; }
    public function set name(value:String):void
    {
        _name = value;
        nameChanged.dispatch();
    }
}

モデルは特に何も継承せず、普通のモデルクラスを書いてください。

View

UserView.as
public class UserView extends Sprite
{
    private var _nameTextField:TextField;
    public function UserView()
    {
        _nameTextField = new TextField;
        addChild(_nameTextField);
    }

    public function changeName(name:String):void
    {
        _nameTextField.text = name;
    }
}

ビューも Flash らしい普通の作り方で問題ありません。
一つ重要なのは UserView#changeName が public に実装されていることですね。

Mediator

UserViewMediator.as
public class UserViewMediator extends Mediator
{
    [Inject]
    public var view:UserView;
    [Inject]
    public var user:User;

    public override function initialize():void
    {
        user.nameChanged.add(changeView);
    }

    public override function destroy():void
    {
        user.nameChanged.remove(changeView);
    }

    public function changeView():void
    {
        view.changeName(user.name);
    }
}

これが User と UserView の仲介を行うメディエータです。
「ユーザの名前が変更された」というきっかけで「ビューにその名前を反映する」タスクを実行するだけのクラスです。
このクラスが Model-View の IoC において最も重要なものです。

Configuration

MainConfig.as
public class MainConfig implements IConfig
{
    [Inject]
    public var injector:IInjector;
    [Inject]
    public var mediatorMap:IMediatorMap;
    [Inject]
    public var contextView:ContextView;

    public function configure():void
    {
        injector.map(User).asSingleton();
        mediatorMap.map(UserView).toMeidator(UserViewMediator);

        contextView.addChild(new UserView);
    }
}

そしてこのクラスが、MVC の登場人物の依存関係のコンフィギュレーションを行うクラスです。
次のようなことを設定しています。

  • User はシングルトンである
  • UserView の対になるメディエータは UserViewMediator である

Application

Application.as
public class Application extends Sprite
{
    public function Apllication()
    {
        var context:Context = new Context()
            .install(MVCSBundle)
            .configure(MainConfig)
            .configure(ContextView(this))
        context.initialize();
    }
}

この Application クラスは Flash のエントリポイント用のルート Sprite です。
Robotlegs の初期化のお決まりの書き方となります。(拡張を導入するときはこの辺りに手を入れる)

以上が Robotlegs の基本的な使い方でした。

動作するサンプルコード

中くらいのサイズのサンプルコードとして使えそうなコードを github に上げています。合わせてご覧ください。
https://github.com/tosik/zenith