Cairngorm: Flex/AIR RIA構築用フレームワーク

Cairngorm Frameworkは、Flash開発元の adobe が提供している、もっとも長い歴史を持つ、もっとも成熟している、冒頭でも書いたとおりFlexアプリケーションにおけるクライアントサイド、Flex/AIR RIA構築用フレームワークです。 オープンソースであるため、誰でも無償で使用することができます。また、Apache License 2.0で配布されているため商用利用も可能です。

※Cairngorm Frameworkの名称の由来:Cairngorm とは、日本語で「煙水晶」という意味です。これはスコットランドにあるCairngorm山が煙水晶の産出地であったことに由来しています。 iteration::two社もスコットランドにあるので、それにちなんでフレームワークの名称にCairngormを採用したのではないかと筆者は推測しています。

Flexはプレゼンテーション層のみを対象とする製品のため、Flexアプリケーションを開発する際には、ビジネス層にSpringやSeasarなどのフレームワークを適用することも多いかと思います。また、Flexと同じプレゼンテーション層のフレームワークですが、既存のアプリケーションを生かすためにStrutsをFlexと併用させるケースもあると思います。

Cairngorm Frameworkは、クライアントサイドのフレームワークのため、動作環境はTomcatなどのアプリケーションサーバ上ではなく、Webブラウザ上の Flash Playerになります。また、StrutsなどのWebアプリケーションフレームワークと違って画面制御やバリデーション、例外ハンドリングといったアプリケーション開発を行うための基盤の機能を提供するフレームワークではありません。

Cairngormフレームワークに関連して経験豊かなJava開発者であるOlesen氏は以下のように述べている。

Cairngormにはmediatorやsupervising presenter、passive viewといった概念やそれに類するものは一切ありません。実際のところ、ビューコンポーネントの状態を直接モデルにデータバインディングすることを推奨していることでCairngormは多くの人に非難されています。さらに悪いことにモデルはグローバルな状態にあり、シングルトン(なクラス)を通して提供されます。

Cairngorm Frameworkの特徴

・オブジェクト指向

 Cairngorm Frameworkは、すべてAction Scriptのクラスまたはインターフェイスとして作成されています。スクリプトと聞くと手続き型のプログラミングをイメージしてしまいがちですが、最近ではオブジェクト指向プログラミング(OOP)の機能を搭載したスクリプト言語も増えています。Flex 1.5で採用しているAction Scriptのバージョンは2.0ですが、Action Scriptはバージョン2.0からこのOOPに対応しました。そのため、バージョン1.0までは手続き型でしか書くことができなかったAction Scriptも、JavaやC#と同じようにクラスやインターフェイスを定義するといったことが可能になりました(もちろん、従来どおり手続き型で書くこともできます)。ちなみに、Ruby On Railsの登場でいま非常に人気が高まっているRubyやWebサイトの構築によく使われているPHPもスクリプト言語ですが、OOPに対応しています。

・デザインパターンのコラボレーション

 Cairngorm Frameworkは、前述したとおり完全にオブジェクト指向に基づいています。そのため、Javaなどでおなじみのデザインパターン(主にJ2EEパターン)を多数利用しています。Cairngorm Frameworkのクラスを眺めてみると“Command”、“Service Locator”、“FrontController”、“BusinessDelegate”などパターン名がそのままクラス名やインターフェイス名になっていることが分かります。
 J2EEパターンでは、より複雑で規模の大きな問題に対処するために複数のパターンを組み合わせて使用することをMicro Architecture(マイクロアーキテクチャ)と名付けていますが、Cairngorm Frameworkはまさにこのマイクロアーキテクチャに基づいています。

・イベント・リスナモデル

 イベント・リスナモデルとは、イベントを発生させる可能性があるオブジェクトをリスナに登録しておきマウスクリックなどのイベントが発生した場合、イベントの発生をリスナに通知し処理をリスナに委譲させる方式です。リスナ(Listener)はその名のとおり、イベントの発生を聞く(捕捉する)オブジェクトです。

 例えばVisual Basicでは、ボタンの中にonClickなどのイベントをハンドリングするメソッドがあり、そのメソッドの中にボタンがクリックされたときの処理を書きました。しかし、これではビューであるボタンの中にボタンを押されたときのロジックが入り込んでしまい、ビューとロジックが密接に関係してしまいます。

 イベント・リスナモデルでは、イベントが発生すると登録先のリスナがそのイベントを処理するので、イベントを発生させるビューとイベント処理のロジックを切り離すことができオブジェクトの独立性が高まります。Flex自体にもイベント・リスナの仕組みはサポートされていますが、Cairngorm Frameworkではよりそれを使いやすくしてフレームワークとして提供しています。

・軽量で導入がカンタン

 Cairngorm Frameworkは前述したとおり、特定のアプリケーションサービスを提供するためのライブラリ群などを持たず、処理の枠組みだけを提供する “Architectural Framework”のため非常に軽量なフレームワークになっています。APIリファレンスですべてのクラスを見てもたった12個しかありません。 Cairngorm Frameworkを使うために必要なものは、フレームワークを構成するクラスをアーカイブしたファイルとCairngorm Frameworkの設定ファイルの2つだけです。導入は、これらをアプリケーションの所定の位置に置き、Flexの設定ファイルを少し変更するだけですのでとてもカンタンです。

オブジェクト指向の恩恵を享受できる

Cairngorm Frameworkは、完全にオブジェクト指向に基づいていますのでJavaやC#などと同様に継承、カプセル化、ポリモーフィズムなどオブジェクト指向のテクニックを使うことができます。

 例えば、各クラスに共通して実装されている処理を親クラスとして切り出し、継承を使ってサブクラスでは親クラスと差異部分のみをコーディングすれば親クラスの再利用性が高まります。また、ポリモーフィズムを使ってインターフェイスと実装を分けるプログラミングをすれば、実装クラスを後で容易に切り替えることができ保守性が上がります。

アーキテクチャが統一される

 Flexに限らずリッチインターネットアプリケーション(RIA)においては、HTMLベースのWebアプリケーションと違ってクライアントサイドにも画面回りのロジックやクライアントサイドだけで完結できるビジネスロジックを実装します。従来のFlexアプリケーションのクライアントサイドの開発では、Flexの仕組みだけを使って開発するか、または独自のフレームワークを作るしかありませんでした。独自のフレームワークといってもJavaではなくActionScriptで作らなければならないため、“処理の枠組み”を作り出すことはかなり難しいのではないかと思います。

 小規模なアプリケーションを少人数で開発する場合なら、Flexの仕組みだけで開発してもさほど問題はないと思います。しかし、大規模なアプリケーションを大人数で開発するときには、何かしらの枠組みがないと個々の開発者が自由にコーディングしてそれぞれのアーキテクチャを作り出すことができてしまいます。その結果、“作った人にしか分からないトリッキーなコード”が量産されてしまいます。これでは、プログラムが作った人に依存してしまい、保守性が下がってしまいます。もし、そんなプログラムの保守を自分がすることになったら……。もうゾッとしてしまいますよね?

 Cairngorm Frameworkは、“Architectural Framework”であり処理の枠組みを定めているため、個々の開発者はフレームワークで決められたアーキテクチャに沿って開発をすることになります。アーキテクチャに沿った開発とは、例えば、フレームワークのクラスを継承してあるメソッドをオーバーライドしたり、フレームワークのインターフェイスを実装してあるメソッドを実装するということです。従って、プログラマーが自由にコーディングすることを制限し、プログラミングに関して個人の裁量の入り込む余地が少なくなっているため、誰が作ってもアーキテクチャが統一され、品質の均一化が望めます。また、プログラムから属人性が排除されるため、保守性の向上にもつながります。

JavaエンジニアのActionScript学習コストを低減できる

 Flexアプリケーション開発では、クライアントサイドをMXMLとActionScriptを用いて作り込む必要があります。クライアントサイドのロジックについては、ActionScriptを使って書きますので、ActionScriptをいかに使いこなすかがFlexアプリケーション開発の1つのポイントにもなります。ActionScriptは、JavaScriptの国際標準規格であるECMA(エクマ)に準拠しているため、 JavaScriptと文法が非常に似ておりJavaScriptのスキルを持っていればActionScriptも理解しやすいということがあります。

 しかし、FlexがJ2EEアプリケーションサーバ上で動作するということもあって、Flexアプリケーション開発を行う開発者の多くは、 Javaエンジニアであり、それほどJavaScriptに明るい人が多いわけではないのが現状です。そのようなJavaエンジニアにとって ActionScriptをマスターするのは、かなり時間がかかり学習コストは高いといえます。

 Cairngorm Frameworkを使えば、Javaエンジニアにとってなじみの深いオブジェクト指向やデザインパターン(J2EEパターン)などに基づいて開発を行うことができますので、すでに持っている開発ノウハウを生かすことができActionScriptの学習コストを低減させることができます。

Cairngormサンプル解説(Service層)

Service層では「Serviceパッケージ 」と「Delegateクラス」で構成されています。

1) Serviceパッケージに配置されるクラスは ServiceLocatorタグを持ったMXMLである ApplicationService.mxmlのみです。
これはCairngormがHTTPServiceやRemoteObjectといった 「サーバサイドとの連携の入り口を1つにしてしまおう」 との意図があります。 サーバサイドとの連携はHTTPServiceやRemoteObjectを利用しますが、 それらを管理しているのが、このApplicationService.mxmlファイルです。 中身を見て見ましょう。




タグのIDが一致しないと、 * HTTPServiceのインスタンスは取得できませんので気をつけてください。 **/ public static const APPLICATION_HTTP_SERVICE_NAME:String = “appHttpService”;    ・・・・② ]]>


①ServiceLocatorタグ
  ③にて記述されるHTTPServiceをCairngormが管理するために用意されたタグが
  このServiceLocatorです。
  このタグの中で宣言されたHTTPServiceやRemoteObjectタグは
  ServiceLocatorクラスより取り出すことができます。
  例)
  var httpSrv:HTTPService = ServiceLocator.getInstance().getHTTPService(“appHttpService”);

②public static const APPLICATION_HTTP_SERVICE_NAME

  ここで宣言されているpublic static constの文字列は
  ③で記述されているHTTPServiceタグ(あるいはRemoteObjectタグ)のIDの文字列と
  一緒である場合が必要です。
  上記①で記したようにServiceLocatorからHTTPServiceあるいはRemoteObjectを取り出す際に、
  ServiceLocatorタグの中で宣言したHTTPServiceタグ(あるいはRemoteObjectタグ)のIDを用いて、
  ServiceLocatorタグから取り出すからです。
  このIDは外部からのアクセス(例えばDelegateクラスからのアクセス)ではIDを
  取得することが難しいです。
  しかし、public static constとして宣言しておけば、
  わざわざタグのIDを調べなくても、この宣言を利用するだけで取り出せるようになるからです。
  ですので、必ず宣言しましょう。

③HTTPServiceタグ
  
HTTPServiceを行うタグです。
  resultにはevent.token.resultHandler(event)
  faultにはevent.token.faultHandler(event)
  と指定されていますが、これはDelegateの中で使う際に指定するもので、
  IResponderのresultメソッドとfaultメソッドがそれぞれ関連付けされるようになっています。
  詳しくはDelegateの解説をご覧ください。

2) DelegateクラスはHTTPServiceやRemoteObject等、サーバサイドと連携を行うためのクラスです。
Cairngormではこの
「Delegate以外ではサーバサイドとの連携ロジックは組み込まない」
というルールになっています。

では、サンプルのDelegateクラスを見てみましょう。

package sample.service.samplecairngorm
{

import mx.rpc.IResponder;
import mx.rpc.http.HTTPService;
import com.adobe.cairngorm.business.ServiceLocator;                       ・・・・・・・・・・・・・・・・・・・・・①

import sample.service.ApplicationService;

/**
* サーバと連携するためのクラスです。
*/
public class SamplecairngormHttpDelegate
{
private var _responder:IResponder;
private var _httpService:HTTPService;

/**
* コンストラクタです。
* @param callingCommand サービス実行結果を返却する先を示すオブジェクトIResponderです。
*/
public function SamplecairngormHttpDelegate(callingCommand:IResponder)   ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・②
{
// ApplicationServicecからHTTPServiceのインスタンスを取得します。
_httpService = ServiceLocator.getInstance().getHTTPService(ApplicationService.APPLICATION_HTTP_SERVICE_NAME);
// 返却オブジェクトを保持します。
_responder = callingCommand;                              ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・③
}
/**
* Commandクラスから呼び出されるサーバ連携用メソッドです。
* @param o サーバーに渡したい値などを入れたObjectクラスです。
*/
public function clickBtnHelloWorld(o:Object):void                     ・・・・・・・・・・・・・・・・・・・・・・・・・・④
{
// 返却オブジェクトのTOKENを指定するため、一時的にObject型に変換しています。
var service:Object = _httpService;
// URLを指定します。
_httpService.url = “./hello.xml”;
// アクセスした結果をserviceに返却し、そのserviceのresultHandlerとfaultHandlerのtokenに設定します。
service = _httpService.send();
service.resultHandler = _responder.result;
service.faultHandler = _responder.fault;                   ・・・・・・・・・・・・・・・・・・・・・・・・・・・・⑤
}

}

}


①インポート文
  Delegateクラスでは、サーバサイドとの連携結果を返すためのオブジェクト設定のためにIResponderを、
  サーバサイドとの連携をするためのクラス保持のためにHTTPService(あるいはRemoteObject)クラスを
  そしてCairngormの中で管理されたHTTPServiceあるいはRemoteObjectを取得するためにServiceLocatorクラスを
  それぞれインポートしています。

②コンストラクタ
  コンストラクタの引数としてIResponderを受け取っていますが、
  これはHTTPServiceやRemoteObjectのメソッドを実行した結果を返すオブジェクトを
  設定するためです。
  このIResponderに指定されたresultメソッドあるいはfaultメソッドに
  サーバサイドの実行結果が返却されるようになっています。

③サーバサイド連携オブジェクトの取り出しと返却オブジェクトの設定
  ServiceLocatorからHTTPServiceあるいはRemoteObjectを取り出しています。
  また引数として受け取ったcallingCommandを
  インスタンス変数に設定しています。
  このことにより、サーバサイドのサービス実行結果がcallingCommandのresultメソッドあるいはfaultメソッドに
  返却されるようになります。

④サーバサイド連携メソッド
  実際にサーバサイドと連携するためのメソッドです。
  Commandのexecuteメソッドの中でこのメソッドが呼び出されることでしょう。
  引数として渡されているObjectクラスは
  HTTPServiceやRemoteObjectに渡すリクエストのパラメータを持ったObjectです。
  例として
  _httpService.request = o;
  と使えると思います。

⑤返却先の再設定
  実際に取り出されたHTTPServiceあるいはRemoteObjectの結果ハンドラを設定しているのがこの部分です。
  service.resultHandler = _responder.result;
  service.faultHandler = _responder.fault;
  とサービスオブジェクトのそれぞれのハンドラに設定していることがわかります。

※サーバサイド連携メソッドの中でプログラムで意識しなければいけないところは
  service.url
  か
  service.request
  のどちらかになります(HTTPServiceの場合)
  それ以外のコーディングはお約束として必ず記述をしてください。
  そうでないとうまくIResponderに返却されません。

以上がService層の解説です。
Service層もDelegateが少々、特殊なことをしていますが、
ここはひとつお約束の構文ということで。

Cairngormサンプル解説(View層)

View層は「viewパッケージ」と「helperパッケージ 」のように構成されています。

今回のサンプルでは1)viewパッケージには何もないと思います。 SampleCairngorm.mxmlをそのままviewとして利用しているためです。
一般的に、ViewStackやStateで別コンポーネント化したものが
このviewパッケージに入れる、とお考えください。

ここでは2)helperパッケージについて解説します。

2)
helperパッケージは
「View層の画面表示用MXMLのScript部分をそのまま集めたクラスをViewHelperと呼ぶようにした」
といえるのではないでしょうか。
Cairngormの根本的な考え方として
「ScriptタグはMXMLの中にはできるだけ書かないようにしよう」
というのがありましたよね。
MXMLの中でScriptで書いていたものがこのhelperパッケージで設定されるViewHelperクラスになります。
このViewHelperクラスはViewHelperというクラスを継承したクラスはCairngormの中で一元管理されます。
一元管理されるということなので、
「ViewHelperにはユニークなIDを割り当てなければならない」
ことに気をつけてください。
サンプルのSampleCairngorm.mxmlの中では
<viewhelper:SamplecairngormViewHelper id=”sampleViewHelper”/>
と宣言されています。
ここで指定したIDがCairngormの中で(つまりは1つのFlexプロジェクトの中で)ユニークになっていなければなりません。
実際に見てみましょう。
例えばサンプルプロジェクトに
「Test1.mxml」
というMXMLコンポーネントを作成し以下のように記述します。



そしてこの「Test1.mxml」を
「SampleCairngorm.mxml」に追加してみましょう。
例)SampleCairngorm.mxmlの一部

                       ・
                       ・


                       ・
                       ・

&bold(){}

これをコンパイルしてみましょう。
コンパイルは無事に通るかと思いますが、
実行してみるとエラーが発生するはずです。
それは
「sampleViewHelperという名前は既に使われているので、名前を変えてください」
といった意味のエラーが出るはずです。
このようにViewHelperのIDは必ずユニークでなければいけません。

さて、肝心のViewHelperクラスの中身ですが

package sample.view.samplecairngorm.helper
{

import com.adobe.cairngorm.view.ViewHelper;
import com.adobe.cairngorm.control.CairngormEventDispatcher;               ・・・・・・・・・・・・・①

import flash.events.*;

import sample.controller.samplecairngorm.event.ClickBtnHelloWorldEvent;       ・・・・・・・・・・・・・・②

/**
* Samplecairngorm画面用のViewHelperです。
*/
public class SamplecairngormViewHelper extends ViewHelper               ・・・・・・・・・・・・・・・・・・③
{
public function SamplecairngormViewHelper()
{
super();
}
/**
* Samplecairngorm画面のボタンID「btn」というUIコンポーネントの
* clickイベントをハンドルするためのメソッドです。
*/
public function clickBtnHelloWorld(event:MouseEvent):void
{
// ここでは単純にイベントをDispatchしていますが、
// この中でVaridationを行うこともできます。
// またViewHelperクラスは「view」というプロパティを持っていて、
// このViewというプロパティはViewHelperクラスをインスタンス化した際の
// MXMLのインスタンスを示しています。
// このプログラムで「View」プロパティが示すMXMLのインスタンスは
// 「SampleCairngorm.mxml」のインスタンスです。
// MXMLのインスタンスですので、そのMXMLに書いてあるUIコンポーネントのインスタンスを取得することもできます。
// 例)
// SampleCairngorm.mxmlのLabelのインスタンスを取得し、textプロパティを取得したい
// var str:String = SampleCairngorm(view).lbl.text;
// こんな風に直接、Viewから値を取得することも可能です。
CairngormEventDispatcher.getInstance().dispatchEvent(new ClickBtnHelloWorldEvent(new Object()));  ・・・④
}

}

}

① CairngormEventDispatcherのインポート
  CairngormEventDispatcherというのは
「Cairngormがイベントとして認識するものを送出するためのクラス」
  です。
  このクラスを利用してイベントを送出しなければ、
  FrontControllerクラスを継承したControllerクラスはイベントを受け取ってくれませんし、
  イベントにマッピングされたロジックも実行されませんので、注意が必要です。

② ClickBtnHelloWorldEventのインポート
  FrontControllerクラスを継承したControllerクラスでpublic static constに設定したイベント文字列を持つイベントをインポートしています。
  これがCairngormEventになるわけです。

③ ViewHelperを継承したSamplecairngormViewHelperクラスの宣言
  CairngormにViewHelperクラスとして認識させるために、ViewHelperクラスを継承したクラス宣言を行っています。

④CairngormEventDispatcher.getInstance().dispatchEventメソッドを使ってのイベント送出
  このメソッドを発行することにより、
「FrontControllerクラスを継承したクラスでaddCommandしたCommandクラスを呼び出す」
  ことをしています。
  このことにより、該当するイベントにマッピングされたCommandクラスのexecuteメソッドが呼び出されるわけです。

以下のURLでダウンロードできます。

http://opensource.adobe.com/wiki/display/cairngorm/Cairngorm;jsessionid=1B8BB89807B455D98ED33F55CC3D6EF3

1 thought on “Cairngorm: Flex/AIR RIA構築用フレームワーク”

Leave a Reply

Your email address will not be published. Required fields are marked *