PureMVC - RIAxDNP

PureMVC
for AS3 ver2.0.4
フレームワーク解説
ver0.9.2
更新履歴
日付
バージョン
更新内容
2009/05/20
0.9
新規作成
2009/06/22
0.9.1
Mediatorの動的作成を追加
2009/12/21
0.9.2
一部公開用に修正
フレームワーク導入の効果
►
MVCを分離し、それぞれのレイヤーの役割を明確化。
 導入するだけで既にMVCモデルの基本構造は実現可能
►
処理パターンに従うことで、実装方法が標準化
 プログラマのスキル及び創意工夫による品質のばらつきが解消され、全
体の品質が向上。
 可読性向上
►
プロジェクトの生産性向上及び開発工数の削減
PureMVC
►
訴求点





フリー、オープンソース
MVCモデルに準拠
シンプルで軽い
AS2、AS3, Java, C#、iPhone(Objective C)、Ruby向けもある
Cairngormの評判が悪いので…
フレームワーク
►
Cairngorm
 http://opensource.adobe.com/wiki/display/cairngorm/
►
easyMVC
 http://projects.simb.net/easyMVC
►
Guasax
 http://www.guasax.com/guasax/web/en/index.php
►
Mate
 http://mate.asfusion.com/
►
Model-Glue
 http://www.model-glue.com/flex.cfm
►
Parsley
 http://www.spicefactory.org/parsley/
►
Penne
 http://www.flexpasta.com/index.php/category/penne-framework/
►
Pure MVC
 http://puremvc.org/
►
Swiz
 http://code.google.com/p/swizframework/
►
Flight
 http://www.flightxd.com/flightframework
►
yui-frameworks
 http://akabana.sandbox.seasar.org/ja/products/yui/index.html
Cairngorm
►
特徴
 Adobeが開発しているFlexのフレームワーク
 Eventをトリガーにビジネスロジックをハンドリング
►
利点
 View、コントローラ、ビジネスLogicの完全分離。
 ビジネスLogicがイベントとセットで完全に独立しており、他の層に対して
疎結合。
 ビジネスLogicを再利用する必要がある要件との親和性が高い。
►
欠点
 学習コストが高い。
 1機能単位あたりの必要クラス数が多く、保守性上昇の代わりに生産性
が低下する。
PureMVC
►
特徴
 必要最低限で軽い
 Eventではなく、オブザーバーパターンのNotificationを使って複数コン
ポーネント間をハンドリング
 シンプルなので拡張やメンテナンスがしやすい
►
利点
 学習コストが低い
 対応している言語が多いので一度覚えれば、他でも使える
 Cairngormで多くできたEventが減らせる
►
欠点
 柔軟性がある反面あいまいな部分もある。⇒ローカルルールが必要
 大規模開発にはカスタマイズが必要
 ハンドリングが文字列依存
PureMVCのクラス(org.puremvc.as3)
puremvc
View
Observer
IObserver
View
Facade
IMediator
IController
IFacade
Mediator
Controller
INotifier
ICommand
IModel
Notifier
Model
IProxy
Proxy
SimpleCommand
INotification
MacroCommand
Notification
≪implements≫
≪extends≫
ValueObject
Responder
ServiceDelegate
概念図
出典:puremvc.org
概念図その2
出典:flex_framework @ ウィキ
Facade
►
►
►
以下、赤字はローカルルールです。
フレームワークの中心
シングルトン
Facadeを介してModel、 View、 Controllerを参照
 Proxy、Command、Mediatorを登録する(register)
 Proxy、Command、Mediatorを削除する(remove)
 Proxy、Mediatorを参照する(retrieve)
►
MVCのコアクラスを隠蔽
コアクラス
►MVCの各層を管理するコアアクター。
►さらに上位の管理クラスであるFacadeで管理されている。
 View
►
►
MediatorとObserverの管理
シングルトン
 Controller
►
►
Commandの管理と実行
シングルト
 Model
►
►
Proxyの管理
シングルトン
フレームワークのコアクラスですが、これらのクラスはFaçadeを介してやりとりされるの
で直接使用することはありません。
【View層】 View ComponentとMediator
►
View Component
 UI部分。通常MXMLで記述される。
 Viewは、その他のレイヤーを参照しない
►
►
Mediatorを介してPureMVCにアクセスする。
ViewからMediatorはイベントでハンドリング。
 View内で簡潔するロジックはViewに記述する。
►
Mediator
 ViewHelper、Viewとフレームワークの橋渡し役
 Viewを内部に保持していて、Viewを制御する
►
ユーザイベントをNotificationに紐付け
 Viewからディスパッチされたイベントをハンドリングし、 Notificationを送る
►
NotificationをViewに反映
 FrameworkからNotificationを受け取り、Viewを制御
 Model(Proxy)の参照を行う。
 listNotificationInterests
►
Mediatorが扱うNotificationリスト
 handleNotificationl
►
Notificationが渡されたときの処理
 View ComponentのSub Componentは各々のMediatorを作成せず、できるだ
け親のMediatorでハンドルする。
【 Controller層】 Command
►
Command
 Notificationを受け処理を実行するBusinessロジック部
 Commandクラスを継承し、executeメソッドを実装する。
 必要なパラメータは、 Notificationから取り出す(getBody)
► SimpleCommand
 1つのコマンド
► MacroCommand
 複数コマンド実行
► addSubCommandでコマンドを登録。
 前のコマンドの結果を待つわけではない。
► SimpleCommandをループでexecuteしているだけ。
【Model層】 DataObject とProxy
►
DataObject
 PureMVCでは、ProxyクラスがDataObjectを保有する。
►
Proxy
 Domainロジックを実装するためのクラス
 ModelとModelを出し入れするためのコントロールを備えている。
► CairngormではModel層はデータの入れ物だっただけ
 データのまとまりごとに作成。
► LoginInfoProxy,
SharedObjectProxy, UploadFileProxy
 サーバサイドの処理はDelegateに依頼する。
Proxyから発行されるNotification
►
ProxyからNotificationが発行される
⇒MediatorがNotificationを受け取り、Viewに結果を反映する。
 Notification例:
►
DataObjectを更新したらNotificationを投げる
(成功失敗がない単純な値やリストの更新カレントアイテムの変更、リスト、フラグの更新など)
⇒Viewの更新、フィードバック、ステートの変更など
►
処理が成功したらNotificationを投げる。
⇒処理成功のフィードバック、画面遷移、ステートの変更など
►
処理が失敗したらNotificationを投げる。
⇒エラーのフィードバックなど
※同一Modelを複数箇所から参照している可能性があるので変更時には注意する。
【Service層】 Service Delegate
►
►
►
►
►
MVCSのS
サーバサイドのロジックとのやりとりを記述する
Proxyの依頼を受けて処理を実行
resopnderで結果をProxyに返す
resultHandler
 通信成功
► 結果:成功
► 結果:失敗
►
faultHandler
 通信失敗
ObserverとNotifier
►
►
►
PureMVCでは、 Observer-Notificationパターンを使用。
Event-EventDispatcherの換わり
MVCフレームワークとその他を疎結合にする
►EventとNotification
Event
►EventはFlash
Platform依存
►バブリングの機構も親子階層の中でしか使えない。
►厳密に型を定義しないといけないので面倒
Notification
►NotificationはPublish/Subscribeパターン
►複数のObserverが同じNotificationを受け取れる。
►オプション値のBodyはObject型。型を気にしないでいいので楽チン。
►Event依存でないので他の言語やプラットフォームとの親和性があがる。
Notification
►
Notifierによって送信される。
 Mediator, Proxy, (SimpleCommand, MacroCommand, Façade)
※Notificationの発行は、Mediator、Proxyからのみとします。
►
Notification(name:String, body:Object = null, type:String = null)
 name
Notificationを識別する名前
► アプリケーション内でユニーク
► 定数クラスで管理⇒PureMVCConst.as
►
 body
►
ValueObjectを乗せる入れ物
 例:LoginUserParam.as(userId、password)
 type
Notificationのタイプを区別したいときに使用する。
► 同じような処理や機能で、commandを複数作る必要もないとき。
►
 例:sendNotification(PureMvcConst.CALL_SQLCONNECTION, file,
SQLConnectionCommand.OPEN);
Notification名について
►
定数クラスで管理⇒PureMVCConst.as
 コマンドの呼び出し通知
►
主にMediatorが発行する
► callを付ける
 例: public static const CALL_LOGIN:String = "callLogin“
► Commandと紐付けが必要
 例: facade.registerCommand(PureMVCConst.CALL_LOGIN, LoginCommand);
 単純な値やリストの更新通知
►
主にProxyが発行する
► update/changeを付ける
 例: public static const CHANGE_CURRENT_ITEM:String =
“changeCurrentItem“
 処理成功/失敗通知(非同期処理)
主にProxyが発行する
► 成功:successを付ける
►
 例: public static const SUCCESS_LOGIN:String = "successLogin";
►
失敗:failureを付ける
 例: public static const FAILURE_LOGIN:String = "failureLogin";
BodyとTypeの使い方
►
コマンドの呼び出し通知時
 body
►
リクエストパラメータ(ValueObject)
 type
►
►
処理タイプ、IDなど
単純な値やリストの更新通知時
 body
►
なし
 type
►
►
なし
処理成功通知時
 body
►
なし
 type
►
►
なし
処理成功/失敗通知
 body
►
エラーオブジェクト
 type
►
なし
Mediatorの登録について
►
MediatorManager
 createMediator()
► Viewのインスタンス化完了時に呼び出し、対応するMediatorを生成する。
お作法どおりだと
インスタンスへの参照を渡さないとい
インスタンスへの参照を渡さないとい
けない⇒レイアウトを変更し難い
けない⇒レイアウトを変更し難い
// View:LoginPanelとLoginPanelMediatorのひも付け
facade.registerMediator(new LoginPanelMediator(app.loginPanel));
facade.registerMediator(new ResultPanelMediator(app.resultPanel));
変更後
例)Main.as
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx=http://www.adobe.com/2006/mxml
creationComplete="init()" >
<mx:Script>
<![CDATA[
import practice.extended.MediatorManager;
インスタンス化完了時に
インスタンス化完了時に
private function init():void
インスタンスを渡す
インスタンスを渡す
{
MediatorManager.createMainMediator(this);
}
]]>
</mx:Script>
</mx:Canvas>
例)MediatorManager.as
public static function createMainMediator(targetViewComponent:Object):void
{
var mainMediator:MainMediator = new MainMediator(targetViewComponent);
ApplicationFacade.getInstance().registerMediator(mainMediator);
}
Mediatorの動的な生成
►
Mediatorは、基本的にはシングルインスタンス
 mediatorの識別IDであるmediatorNameはconst
►
同一クラスから複数のView componentを生成する場合、対応するMediatorも複数
生成する必要があります。
 mediatorの識別IDを動的に付与
例)ItemComponentMediator.as
public class ItemComponentMediator extends Mediator
{
接頭辞をconstで指定
接頭辞をconstで指定
// Mediatorの名称の接頭辞
public static const NAME_PREFIX:String = 'ItemComponentMediator';
/**
* コンストラクタ
* @param mediatorName
コンストラクタで、mediatorName
コンストラクタで、mediatorName
* @param viewComponent
(要ユニーク)を動的に付与。
(要ユニーク)を動的に付与。
*
*/
public function ItemComponentMediator(mediatorName:String, viewComponent:Object)
{
super(mediatorName, viewComponent);}
}
Mediatorの動的な生成
例) AddItemCommand.as
override public function execute(notification:INotification):void
{
var uid:String = UIDUtil.createUID(); // ユニークID DBのuidなど。
var itemVO:ItemVO = new ItemVO(uid);
要求時にCommandを呼び出し
要求時にCommandを呼び出し
// Proxyにアイテムを追加する
_itemProxy.addItem(itemVO);
}
/**
* アイテムを追加する。
* @param itemVO アイテム
* itemVOからview componentとそのmediatorを動的に生成し、紐付ける
*
*/
public function addItem(itemVO:ItemVO):void
{
// modelの追加
_itemList.addItem(itemVO);
Proxyで、Model/View/Mediatorを一緒に
Proxyで、Model/View/Mediatorを一緒に
管理する。
管理する。
// Mediatorの追加
var mediatorName:String = ItemComponentMediator.NAME_PREFIX + itemVO.itemId;
var itemComponent:ItemComponent = new ItemComponent();
itemComponent.name = mediatorName;
itemComponent.itemVO = itemVO;
var itemComponentMediator:ItemComponentMediator
= new ItemComponentMediator(mediatorName, itemComponent);
facade.registerMediator(itemComponentMediator);
// viewの追加
例) ItemProxy.as
var parentComponentMediator:ParentComponentMediator =
ParentComponentMediator.getInstance();
parentComponentMediator.addItemComponent(itemComponent);
}
Proxyの登録について
►
getInstanceメソッド
 Proxyを取得する。なければ生成し、登録する
お作法どおりだと
// Model:Proxyを登録
StartUpCommandで
StartUpCommandで
登録
登録
facade.registerProxy(new LoginProxy());
変更点
例) ItemProxy.as
/**
* Proxyインスタンスを取得する。
* @return
*
*/
public static function getInstance():ItemProxy
{
if (!ApplicationFacade.getInstance().hasProxy(PROXY_NAME))
{
ApplicationFacade.getInstance().registerProxy(new ItemProxy());
}
return ApplicationFacade.getInstance().retrieveProxy(PROXY_NAME) as ItemProxy;
}
(お詫び)
この記述だとProxy同士で相互参照している場合、無限ループになってしまうので注意。
パッケージ構成の例
TIPS
ViewStackなどでは、Viewは要求があったタイミングでインスタンス化されま
す。このタイミングではNotificationのハンドルが間に合わない場合がありま
す。
► Notificationの相互参照で無限ループになりえるので注意。
►
参考
►
Cairngorm
 http://opensource.adobe.com/wiki/display/cairngorm/Cairngorm/
►
PureMVC
http://puremvc.org/
►
flex_framework @ ウィキ
 http://www39.atwiki.jp/flex_framework/