24 August 2009
ページ ツール |
Flexに関する知識が必要です。
上級
制御の反転(IOC:Inversion of Control)は、依存性注入(DI:Dependency Injection)とも呼ばれ、ここ数年の間にソフトウェア開発の分野で注目され始めたデザイン戦略です。これにより、多くのFlexデベロッパーの目が、Spring ActionScript、Parsley、Flicc、Swizといったフレームワークに向けられるようになりました。
IOCとは、簡単に言えば、正しい実装を持つオブジェクトのフィールドを設定する責任を、それらのオブジェクト自体が持つのではなく、別個のオブジェクトが持つようにする手法です。この手法の利点の1つは、オブジェクトのフィールドをインターフェイスとして宣言することにより、オブジェクトを実装から分離できることです(これは契約によるデザインと呼ばれます)。もう1つの利点は、作成ロジックをオブジェクトから取り除くことで、オブジェクトの目的を明確にできることです。
IOCコンテナは、このパターンを一貫した宣言的方法で採用するために役立つフレームワークを提供します。この戦略とインターフェイスを組み合わせることで、テストが容易で柔軟なオブジェクトを作成できます。IOCパターンの詳しい説明については、Martin Fowlerの記事Inversion of Control Containers and the Dependency Injection patternを参照してください。
Javaおよび.NetのIOCフレームワークは既に確立されており、最近ではFlexコミュニティ内部でこれに関する様々な活動が見られます。
この記事では、これらのフレームワークのいくつかを紹介し、その仕組の概要と相互の比較を示します。実装の要件を比較するため、Spring ActionScript、Parsley、Flicc、Swizの各フレームワークを、ProfileViewerという同じベンチマークプロジェクトに適用します。
オブジェクトを設定する最も一般的な方法は、次の2つです。
myObject = new Object())var myObject = registry.getMyObject())IOCでは、アプリケーションオブジェクトを別のレイヤーにインスタンス化し、必要に応じてオブジェクトに依存性を渡します。この手法には、次の2つの一般的な方法があります。
instance.myObject = new Object())instance = new Instance( new Object() ))IOCフレームワークは一般的に、3つの主要部分から構成されます。設定、ファクトリー、注入機構です。
設定とは、オブジェクト相互の関連を記述する部分です。設定を記述する最も一般的な方法は、ファイル内で宣言することです。このファイルは、コンテキストファイルと呼ばれることがあります。設定は、メタデータ/注釈で記述されたり、プログラムで記述されたりする場合もあります。
ファクトリーは、設定を解析し、アプリケーションが実行されたときにすべてのオブジェクトを取得できるように準備します。
クラシックなSpring(Javaの最も一般的なIOCフレームワーク)では、IOCコンテナによって準備されるすべてのオブジェクト(私の用語ではクライアントオブジェクト)は、その依存性をインターフェイスとして宣言します。コンテキストファイルでは、宣言された依存性は特定のインプリメンテーションクラスを使用するように設定されます。
注入機構は、ファクトリーによって作成されたインスタンスが、アプリケーションに注入されたり、互いの中に注入されたりする手段です。
Spring Webアプリケーションでは、これは web.xml ファイルによって実現されます。Springはwebappコンテキストがロードされるのを監視します。そこから、Springはクラスローダーにフックし、作成する必要があるオブジェクトを選び出します。ファクトリーは(必要な場合)オブジェクトをインスタンス化し、そのすべての依存性を(必要ならインスタンス化してから)設定し、その後にインスタンスをアプリケーションに返します(これは「相互接続」とも呼ばれます)。
Flexではクラスのロードの仕組が異なるので、接続も異なる方法で行う必要があります。
現時点ではこれには次の2つの方法があります。
これは、フレームワークをいくつか調べていくうちに明確になっていくはずです。
フレームワークの比較に使用するProfileViewerプロジェクトは、非常に単純なアプリケーションであり、ログインパネルとダッシュボードの2つの画面から構成されます。ProfileViewerは、単純なモデル-ビュー-コントローラー(MVC)アーキテクチャと、プレゼンテーションモデルパターンを採用しています。
注意:このベンチマークアプリケーションは1つの例に過ぎません。コーディングの方法は他にもあります。これは、私がこれまでに見たことがあるいくつかの一般的なパターンに基づいています。私のフレームワークの使用方法が、フレームワークの本来の設計意図と異なると思われる場合は、お知らせください。ご意見や改善のご提案に基づいて、修正を加えていきたいと思います。
すべてのサンプルのソースコードは、Google codeのflex-ioc-examplesプロジェクトにあります。
この後の部分をお読みになるときには、ぜひソースコードを開いておくことをお勧めします。
このアプリケーションで使用されるMVCパターンは、グラフィカルユーザーインターフェイスの開発に広く用いられています。このパターンについてここでは詳しく説明しません。詳しくは、モデル-ビュー-コントローラーページを参照してください。
この上に、私はサービスレイヤーを実装しました(図1を参照)。これは、アプリケーションがバックエンドシステムからデータを取得する統合ポイントです。サンプルでは、アプリケーションのこの部分をスタブにしています。
最後に、プレゼンテーションモデルパターンを使用しています。ここでは、ビューがそのステートとロジックを含むモデルを持っています。ビューは、このモデルのステートの変化に反応します。これは通常、バインディング式を通じて行われます。これにより、ビューロジックのユニットテストが可能になります。詳しくは、Martin Fowlerによるプレゼンテーションモデルの説明またはこのテーマに関するPaul Williamsの投稿を参照してください。
IOCフレームワークを使用するようにProfileViewerを変更するために、オブジェクトインスタンスとその依存性の管理をIOCレイヤーに移します(図2を参照)。いくつかのフレームワークでは、イベントをアクションに結び付けることができます。これにより、コントローラーレイヤーを作成することができます。可能な場合は、フレームワークによって提供されるものを使用します。
以下では、IOCを使用することで改善できるアプリケーションの各部分について簡単に説明します。
ユーザーが正常にログインすると、アプリケーションは2つのオブジェクトを取得します。これら2つのオブジェクトからの詳細が、異なるビューでユーザーに表示されます。ダッシュボードのプレゼンテーションモデルであるDashboardPMを準備する際に、2つのオブジェクトインスタンスの検索を実行します。
MainPMより:
public function set
authenticated( value : Boolean ) : void
{
//..
var locator : ModelLocator = ModelLocator.getInstance();
dashboardPM = new DashboardPM( locator.user, locator.friends );
//..
}
この例で、ModelLocatorはモデルオブジェクトを格納するためのSingletonパターンです。
シングルトンを使用すると、1つのインスタンスしか宣言できないので、アプリケーションのどこからでも同じオブジェクトインスタンスにアクセスできます。この例では、UserとFriendsが、他の場所で用いられているのと同じインスタンスであることが保証されます。
このようにシングルトンは便利ですが、欠点もあります。それは、ユニットテストが難しくなることです。テストスイートの実行期間を通じて、オブジェクトのライフサイクルに注意を払わなければなりません。これは、1つのインスタンスが静的参照で記憶され、複数のテストケースを通じて存在し、ガベージコレクションの対象とならないからです。
アプリケーションでシングルトンを使用することによる問題を最小化する1つの方法は、階層を通じてオブジェクトを受け渡すことです。
その実例は、DashboardPMのコンストラクターにあります。これは、UserおよびFriendsモデルを受け入れます。プレゼンテーションモデルは、Userオブジェクトしか使用しませんが、これらのインスタンスを子に渡します。オブジェクトが、直接に使用しない別のオブジェクトに依存するのは、よい方法とは言えません。
小さいサンプルアプリケーションではこれはそれほど問題になりませんが、アプリケーションの規模が大きくなると、この方法では大幅に手間が増えることがおわかりになるでしょう。これはまた、クラスに不要なノイズを加えることにもなります。必要な項目だけでオブジェクトをインスタンス化する方が簡潔です。
最後に、元のProfileViewerでは、オブジェクトのパススルーが使用できるように、プレゼンテーションモデル内部に階層がありました。IOCを使えばこれは不要になるので、階層を取り除きます。
非ビューレイヤーを設定できる機能は、サンプルアプリケーションに対する有用な拡張です。この例では、これはLoginDelegateクラスで表現されています。これは自分自身のRemoteObjectインスタンスを作成します。
Springの.NetまたはJavaバージョンを使用したことがある方なら、Spring ActionScriptはすぐに理解できます。実行時に設定ファイルをロードします。これによりファクトリーは、アプリケーションが要求したオブジェクトをインスタンス化するための情報を得ることができます。
Spring ActionScriptをベンチマークプロジェクトに使用するには、次の3つの基本的な手順が必要です。
Spring ActionScriptでは、オブジェクトの宣言はXMLファイル(名前は通常 application-context.xml)に追加されます。このファイルはアプリケーションからアクセス可能です。この設定は、ObjectFactoryのサブクラスであるXMLApplicationContextによってロードされます。
この実装では、ContextLoaderとInjectの2つのオブジェクトに初期化を入れています。
ContextLoaderは、アプリケーションコンテキストファイルへのパスを取ります。これはXMLApplicationContextにロードされます。アプリケーションルートには次のコードがあります。
private function init() : void
{
ContextLoader.contextPath = "application-context.xml";
}
ContextLoaderは背後でSpring ActionScriptコンテキストをロードしています。
public static function set contextPath( value : String ) : void
{
_contextPath = value;
applicationContext = new XMLApplicationContext( _contextPath );
applicationContext.addEventListener( Event.COMPLETE, handleLoadComplete );
applicationContext.load();
}
そして、依存性を必要とするビューに、Injectタグを作成してあります(同僚のParsleyによる実装にヒントを得ました)。この便利なタグによって、ビューに追加したい依存性を宣言できます。例えば、アプリケーションルートには次のコードがあります。
<springActionscript:Inject
property="pm"
objectId="{ ContextIds.MAIN_CONTAINER_PM }"/>
<springActionscript:Inject
property="controller"
objectId="{ ContextIds.CONTROLLER }"/>
これは、XMLApplicationContextに対して、idがcontrollerのオブジェクトを、このビューのメンバー変数controllerに適用するように要求します。
これは、後でビューからオブジェクトを取得するためのよい方法です。
注意:Christophe Herremanのブログに、このような注入をメタデータを使って実行する方法(Swizフレームワークに類似)が紹介されています。ただし、この方法はパフォーマンスにやや不利に影響します。メタデータを読み取るためにビューをXMLにシリアル化する必要があるからです。
Spring ActionScriptには、フレームワークに対するMVCS拡張をリリースする計画がありますが、このバージョンでは、私が自分で作成したコントローラーを使用し、Spring ActionScriptにハンドラーをイベントソースにフックアップさせます。
元のアプリケーションでは、コントローラーは表示リストを通じてバブルしてくるイベントを監視します。イベントを受信すると、コントローラーはハンドラーを通じて検索を行い、このイベントを処理できるオブジェクトを見つけます。この変更された例では、表示リストからのイベントを待つ代わりに、イベントソースとイベントハンドラーを application-context.xml ファイル内でペアにします。
このために、イベントソースとハンドラーをペアにするためのControllerPairという新しいクラスを追加しました。これをSimpleControllerに渡すと、SimpleControllerが各ペアをinit 関数内で初期化します。
<object id="controller" class="com.adobe.login.control.SimpleController">
<method-invocation name="init"></method-invocation>
<property name="controllerItems">
<array><ref>controllerItem</ref></array>
</property>
</object>
<object id="controllerItem” class="com.adobe.login.control.ControllerPair">
<property name="dispatcher" ref="loginPM"/>
<property name="handler" ref="handler"/>
</object>
method-invocationタグに注目してください。これは、オブジェクトが構築された後でオブジェクトに対して呼び出す関数を宣言するためのものです。この例では、init()関数を呼び出しています。この関数は、イベントディスパッチャーをイベントハンドラーに結び付けます。
ProfileViewerの非IOCバージョンでは、オブジェクトのパススルーを可能にするため、プレゼンテーションモデルは階層として構成されていました。ここではこの階層を取り除いて、各プレゼンテーションモデルをそのビューに対してだけ設定できるようにします。
これにより、アプリケーションの設定とテストははるかに容易になりますが、欠点もあります。場合によっては、異なるプレゼンテーションモデルの間のインタラクションをフックアップする余計な手間がかかるのです。
Spring ActionScriptは、セッター注入とコンストラクター注入の両方をサポートします。私はコンストラクター注入の方が好みです。オブジェクトが機能する必要がある依存性が明確になるからです。DashboardPMの設定を次に示します。
<object id="dashboardPM" class="com.adobe.dashboard.presentationModel.DashboardPM">
<constructor-arg ref="user"/>
</object>
XMLで宣言されているコンストラクター引数は、オブジェクトコンストラクターが受け取る順序と同じです。refは、コンテキスト内の別のオブジェクト宣言を参照するためのものです。この例では、Userの宣言を参照しています。
LoginHandlerには、代理オブジェクトへの参照が含まれます。このオブジェクトはリモートオブジェクトへの依存性を持ち、リモートオブジェクトがバックエンドの呼び出しを行います。
これはセッター注入によって設定します。ドメインインスタンスと代理クラス、それにAuthenticationClientが渡されます。これはユーザーがログインしているかどうかをチェックするインターフェイスです。AuthenticationClientはMainPMで実装されます。
このインスタンスには、代理オブジェクトのスタブがあります。これにも、リモートオブジェクトがコンテキストファイルで次のように設定されています。
<object id="handler" class="com.adobe.login.control.handler.LoginHandler">
<property name="client" ref="mainPM"/>
<property name="user" ref="user"/>
<property name="friends" ref="friends"/>
<property name="delegate" ref="loginDelegate"/>
</object>
<object id="loginDelegate" class="com.adobe.login.service.LoginDelegate">
<property name="remoteObject" ref="remoteObject"/>
</object>
<object id="remoteObject" class="mx.rpc.remoting.RemoteObject">
<property name="destination" value="SPRING_ACTIONSCRIPT_DESTINATION"/>
</object>
Spring ActionScriptは、アクティブな開発ロードマップを持つ優れた成熟したフレームワークです。使用されている用語は、Springを使ったことがある方には既におなじみのものです。
XMLを使ってオブジェクトを宣言する場合に問題になることの1つは、XMLでクラスを宣言して、そのクラスが(アプリケーションで直接参照されていないために)SWFに含まれない場合、Flash Playerでランタイム例外が発生することです。これを解決するには、コンテキストXMLからの依存性を宣言するActionScriptクラスを作成し、それをアプリケーションに含める方法があります。
Parsleyの核となるのは、コンテキストの概念です。これはSpringから来たもので、アプリケーションの依存性注入設定を指します。
Parsleyの設定には現在、XMLやMXMLなどの様々なオプションがあります。ネイティブMXMLマークアップやカスタムMXMLタグをParsleyライブラリから使用できます。注入機構を実現するために、ParsleyではSwizフレームワークに似たメタデータタグを使用します。
Parsleyにはメッセージングパターンも付属しています。ごくわずかなコードを追加するだけで、オブジェクトをイベントソースやイベントハンドラーとして使用できます。この例では、コントローラーパターンを使用する代わりに、この機能を利用しています。
Parsleyの設定には次の3つの基本手順があります。
Contextの初期化Injectメタデータの追加設定ファイルを準備するにはいくつかの方法がありますが、この例では、MXMLファイルでネイティブマークアップとParsleyタグを使用します。この手法の利点は、クラスをコンパイル時に含められることですが、その代わりにコンパイル済みのアプリケーションの設定を変更することはできません。
Config.mxml には、 ドメインモデルから代理オブジェクトまでのすべてのアプリケーションオブジェクトが存在します。これらは次の2つの方法で宣言されています。
これら2つのタイプについては、後の方で詳しく説明します。
ここでは、自分で作成したコントローラーの代わりに、Parsleyのメッセージングシステムを利用します。これは、ユーザーが作成したオブジェクトに最小限の影響しか与えないように設計されています。これはメタデータを利用しています。Contextから見え、このメタデータを持つオブジェクトに対しては、Parsleyでイベントソースをイベントハンドラーに結び付けることができます。
サンプルアプリケーションで、LoginPMはイベントソース、LoginAction(LoginHandlerの名前を変更したもの)はイベントハンドラーです。
LoginPMの一部を次に示します。
[Event( name="LOGIN", type="com.adobe.login.control.event.LoginEvent")]
[ManagedEvents("LOGIN")]
public class LoginPM extends EventDispatcher
{
...
public function login() : void
{
var event : LoginEvent = new LoginEvent( username, password );
dispatchEvent( event );
}
}
これをイベントソースとして使用可能にするための要素は、Eventメタデータタグ、ManagedEventsメタデータタグ、EventDispatcher#dispatchEventの3つです。この3つのうちで、ManagedEventsだけがParsley固有の追加です。Eventメタデータタグは単によい習慣というだけであり、dispatchEventはこの仕組に必須のものです。Parsleyは、ManagedEventsを使って、イベントハンドラーの処理と委任に必要なイベントを見つけます。
LoginActionの一部を次に示します。これはイベントハンドラーとして設定されています。
public class LoginAction implements IResponder
{
[MessageHandler]
public function execute( event : LoginEvent ) : void
{
...
}
}
この関数にMessageHandlerメタデータを追加したので、Parsleyはこのオブジェクト/関数を、LoginEventタイプのすべてのイベントにリスナーとして追加します。
これらのオブジェクトがParsleyに見えるようにするには、FlexContextBuilderに渡す設定ファイル内でこのオブジェクトを宣言するか、ビュー内でConfigureオブジェクトを使用します。
他の例と同様に、プレゼンテーションモデルの階層を取り除きました。詳しくは、Spring ActionScriptの項目を参照してください。
Parsleyは、セッター注入とコンストラクター注入の両方をサポートします。Spring ActionScriptの例で述べたように、私はコンストラクター注入の方が好みです。オブジェクトが機能するために必要な依存性が明確になるからです。DashboardPMの設定を次に示します。
<spicefactory:Object type="{ DashboardPM }"/>
オブジェクトにコンストラクター引数が必要な場合、Objectタグを使って引数を宣言します。このような引数はネイティブMXMLではサポートされないからです。
コンストラクターを完成するために、クラスにメタデータをいくつか追加します。
[InjectConstructor]
public class DashboardPM
{
public var user : User;
public function DashboardPM( user : User )
{
this.user = user;
}
...
}
メタデータタグInjectConstructorは、Userタイプの宣言済みオブジェクトをDashboardPMのコンストラクターに注入するようにParsleyに指示します。
セッター注入の場合は、Injectメタデータタグをクラスに追加するだけです。例としては、SummaryPMをConfig内部で標準MXMLで宣言しています。
<dashboard:SummaryPM/>
クラスファイルには、次のコードがあります。
public class SummaryPM
{
[Inject]
public var friends : Friends;
...
}
Injectタグは、Friendsタイプのインスタンスをこのインスタンスに注入する必要があることを示します。
他のフレームワークが先鞭を付けた革新的機能の影響を受けて、Parsleyの最新バージョンは完成したIOCフレームワークになっています。また、コンテキストのアンロードが可能なモジュラー開発もサポートします。これは、増加しつつあるモジュールを使用したFlexアプリケーションにとって重要な機能です。
Fliccは、IOCの世界に比較的最近登場したため、知名度はあまり高くありません。これは、MXMLファイルを設定に使用するという、多少異なる手法を採用しています。これはまた、MXMLでのオブジェクト定義のための独自のマークアップを採用しているため、既存のオブジェクトをファクトリーに渡してその依存性を追加させることができます(例えばビューに渡すなど)。
依存性をMXMLで宣言することの利点の1つは、コンパイル時にアプリケーションに依存性が追加されるので、アプリケーションを実行したときに依存性が存在しないためにランタイム例外が発生するおそれがないことです。欠点は、依存性を変更するにはアプリケーションの再コンパイルが必要になることです。
Fliccでは、Configureタグをビューに追加して、設定するインスタンスを渡せるようにします。また、Spring ActionScriptの実装で使用したInjectタグのように、既存のインスタンスを渡して設定させることもできます。
Configureタグを追加します。<FliccListener>
<MxmlObjectFactory>
<Config/>
</MxmlObjectFactory>
</FliccListener>
メインMxmlObjectFactoryタグは、アプリケーションルートに追加されます。これはFliccListenerタグに囲まれており、アプリケーションの表示リスト内にあるConfigureタグからディスパッチされるイベントをFliccが受け取れるようにします。 Config.mxml が独自の設定ファイルです。
Parsleyと同様、Fliccでは設定ファイル内のオブジェクトにイベントを結び付けることができますが、コントローラーの概念は用いられません。
ここではこれを使って、コントローラーをイベントソース(LoginPM)に結び付けています。LoginPMがイベントをディスパッチすると、コントローラーがイベントを受け取り、適切なハンドラーを呼び出します。
<Object objectId="controller" clazz="{ SimpleController }">
<handlers>
<EventHandler eventName="{ LoginEvent.LOGIN }" handler="handleEvent">
<source>
<Ref to="loginPM"/>
</source>
</EventHandler>
</handlers>
<handlerArray>
<List clazz="{ Array }">
<Ref to="loginHandler"/>
</List>
</handlerArray>
</Object>
コントローラーでは、イベントのイベントタイプを検索して、executeを呼び出します。
public function handleEvent( event : Event ) : void
{
getHandlerForEventType( event.type ).execute( event );
}
この例でも、プレゼンテーションモデルの間の階層を取り除きます。
Fliccでは、セッター注入とコンストラクター注入の両方が使用できます。
Fliccには、ComponentとObjectの2つの基本的なオブジェクトタグがあります。Componentは、設定対象の既存のオブジェクトを記述するためのものです。Objectは、新規インスタンスを作成します。例えば、次のようになります。
<Object objectId="dashboardPM" clazz="{ DashboardPM }">
<constructor>
<Ref to="user"/>
</constructor>
</Object>
Spring ActionScriptやParsleyと同様に、オブジェクトコンストラクターの引数は、オブジェクトが受け取るのと同じ順序で宣言します。Refは、to属性で識別されるコンテキスト内で宣言された別のオブジェクトへの参照です。
Componentも同様ですが、セッター注入だけが使用できます。Componentが記述するオブジェクトは、既に存在していてFliccに渡されるものだからです。
<Component objectId="main">
<controller>
<Ref to="controller"/>
</controller>
<pm>
<Ref to="mainPM"/>
</pm>
</Component>
Componentには、メインアプリケーションを設定します。これにはcontrollerとpm変数があります。
LoginHandlerはLoginDelegateに依存しており、これはさらにRemoteObjectに依存しています。
以下に設定を示します。
<Object objectId="loginDelegate" clazz="{ LoginDelegate }">
<remoteObject>
<Ref to="remoteObject"/>
</remoteObject>
</Object>
<Object objectId="remoteObject" clazz="{ RemoteObject }">
<destination>
FLICC_DESTINATION
</destination>
</Object>
以上のコードは、LoginDelegateの新規インスタンスを宣言し、RemoteObjectを設定します。
Fliccは、宣言形式としてMXMLを使用する点で前の2つのフレームワークと異なっていますが、どのフレームワークでも基本的な原則は同じです。
設定をMXMLで宣言することにより、開発の時間を節約できます。クラスがアプリケーションと共にコンパイルされるので、外部ファイルのロードや解析の処理が不要になるからです。APIは前の2つのフレームワークと異なるので、多少慣れが必要です。
Swizは2008年初めに登場したフレームワークで、熱心な支持者がいます。これは単なるIOCではなく、リッチインターネットアプリケーション(RIA)アーキテクチャのための完全なソリューションとなることを目標としています。SwizはMXMLを使用してオブジェクトを定義しますが、注入機構は異なっています。
Swizには、アプリケーションの初期化を容易にするSwizConfigというクラスが用意されています。このクラスは、Swizインスタンスにロードされた設定ファイルを受け取ります。この設定ファイルでは、標準のMXML宣言と、コンストラクター引数を取るオブジェクトを作成するためのカスタムタグPrototypeが使用できます。注入を定義するためには、Swizはクラス自体からメタデータを読み取って、どのオブジェクトをどこに注入するかを決定します。これは、Spring 2.5での注釈のサポートと、後にGoogle Guiceによって、Javaの世界では一般的になった手法です。
メタデータを使うことで、オブジェクトがわかりやすくなり、設定ファイルが多少簡潔になります。
Swizの基本設定
Swizでは、ビーンズをアプリケーションルートにロードします。
<swizframework:SwizConfig
strict="true"
mediateBubbledEvents="true"
beanLoaders="{[ AppBeans ]}"
viewPackages="com.adobe.login.view,com.adobe.dashboard.view"
logEventLevel="{
LogEventLevel.WARN }"/>
これには2つのオプションがあります。Swizにビーンズを要求できます。
Swiz.getBean( "loginHandler" )
または、メタデータを使用できます。
[Autowire]
public var pm : DashboardPM
可能な方法の一覧については、Swizのドキュメントを参照してください。メタデータ注入に関して注意すべきことの1つは、クラスが表示リスト内に存在するか、BeanLoaders内部で宣言されている必要があることです。そうでないと、注入は実行されません。上述のコード断片は、DashboardPanelの一部です。
Swizは背後で表示リストに追加されるオブジェクトを監視しており、リフレクションを使用してオブジェクトに注入メタデータが含まれるかどうかを調べます。このためには、オブジェクトをXMLにシリアル化してメタデータを読み取る必要があるので、パフォーマンスに影響します。この影響を少なくするには、SwizConfigのviewPackagesプロパティを使って、調査の対象とするクラスをパッケージレベルで制限する方法があります。
Swizの便利な機能の1つは、Swizの機構を使ってディスパッチされるイベントを処理する関数を指定できることです。
ここでは、LoginHandlerに次のメタデータを追加しています。
[Mediate(event="LOGIN", properties="username,password")]
public function login( username : String, password : String ) : void
そして、イベントソース(LoginPM)で次のようにイベントをディスパッチします。
dispatchEvent( new LoginEvent( username, password ) );
これが動作するためには、SwizConfigでバブルしたイベントの伝達を有効にする必要があります。これは、ビュー階層を通じてバブルしてくるイベントをSwizが監視することを意味します。バブルしたイベントの伝達を有効にしない場合、次の方法が使用できます。
Swiz.dispatchEvent( new LoginEvent( username, password ) );
ただしこれを使うと、コードがフレームワークに明示的に拘束されます。
ProfileViewerアプリケーションでは真に非同期の要求は行われませんが、Swizにはこのような呼び出しを管理するために役立つクラスがいくつか用意されています。AbstractControllerには"executeServiceCall"というメソッドがあります。これを使えば、動的なResponderオブジェクトを生成でき、この定型的なコードを自分で作成しなくてすみます。また、SwizのTestUtilクラスを使えば、仮のサービス呼び出しと結果を作成できます。
他のフレームワークの場合と同様に、プレゼンテーションモデルを階層なしにしました。定義は次のとおりです。
<local:MainPM id="mainPM"/>
<login:LoginPM id="loginPM"/>
<factory:Prototype
id="dashboardPM"
xmlns:factory="org.swizframework.factory.*"
classReference="{ DashboardPM }"
constructorArguments="{[ user ]}" />
宣言は標準のMXML宣言です。Prototypeタグを使えば、インスタンスを作成するのではなく、オブジェクトを記述できます。これにより、コンストラクター引数をオブジェクトに渡すことができます。結び付けはクラス内で行います。
public class MyClass
{
[Autowire]
public var myDependency : MyModel;
…
}
Swizはタイプによって自動的に結び付けを行います。したがって、一致するタイプが定義されたビーンがちょうど1つだけ存在する必要があります。該当するタイプのビーンが存在しないか複数存在する場合は、ビーン名を指定する必要があります。LoginControllerにはAuthenticationClientへの依存性があります。これはMainPMが実装しているインターフェイスです。
サービスレイヤーの設定
このサンプルでは、LoginHandlerの名前をLoginControllerに変更しています。これは、SwizのAbstractControllerを拡張するためです。
public class LoginController extends AbstractController
{
[Autowire]
public var client : AuthenticationClient;
[Autowire]
public var user : User;
[Autowire]
public var friends : Friends;
[Autowire]
public var delegate : LoginDelegate;
[Mediate(event="LOGIN", properties="username,password")]
public function login( username : String, password : String ) : void
{
executeServiceCall( delegate.authenticate( username, password ), result );
}
}
AbstractControllerにはexecuteServiceCallという関数が用意されています。これは、結果リスナーとフォールトリスナーを非同期呼び出しに自動的に追加します。ユーザーは、結果ハンドラーと、オプションでフォールドハンドラーを指定します。これにより、定型的なコードを自分で書く手間が省けます。
代理オブジェクトは、他のサンプルと多少異なっています。これは1つにはAbstractControllerの動作を示すためですが、もう1つはSwizのTestUtil.mockResultという機能を紹介するためでもあります。このヘルパー関数は、非同期要求を模擬するもので、開発中に呼び出すバックエンドが存在しない場合に便利です。
public class LoginDelegate
{
//only included for illustrative purposes
//to show that swiz has configured it correctly.
[Autowire]
public var remoteObject : RemoteObject;
public function authenticate(
username : String,
password : String ) : AsyncToken { var mockResult : * = {
user : MockModelFactory.createUser( username ),
friends : MockModelFactory.createFriends() }
return TestUtil.mockResult( mockResult );
}
}
Swizでは、新しい手法で注入を定義します。このフレームワークには、定型的なコードの作成の手間を省くなど、開発プロセスを単純化するための様々な機能が備わっており、アプリケーションのビジネス関連の部分に集中できます。
この記事では、Flexフレームワーク用のよく知られたIOCコンテナのいくつかを簡単に紹介しました。ここで説明しなかった重要なフレームワークとしては、MateやSmartyPantsがあります。Mateには、IOCだけでなく、様々な機能があります。実は、この記事を書くためにMateのサンプルを用意しかけたのですが、その前にもう少し勉強が必要だと判断しました。MateのサンプルはGoogle codeで公開する予定です。SmartyPantsは、Swizと同様にメタデータを使って注入を記述するもので、一見の価値があります。
この記事が、第1にFlexアプリケーションでIOCを使用する方法に関するガイドとして、第2に様々なフレームワークの間の比較として、お役に立ったことを祈ります。各フレームワークのすべての機能を紹介することはできませんでしたので、詳しくは各フレームワークのドキュメントを参照することをお勧めします。