由於工作的關係,開始接觸 Adobe 官方建議的微型 MVC 架構 "Cairngorm",剛開始挫折不斷。看了官方的 CairngormStoreWeb 範例教學說明..嗯...看到想睡;在 google 上搜尋了一堆經驗分享文章也都是片片段段,最後終於讓我發現了一個寫的相當簡單易懂的 Getting Started with Cairngorm 教學系列文章 by David Tucker,強烈推薦想要 Cairngorm 快速入門的人一定要看一下!尤其示意圖更是容易理解。
先到 Adobe open source Cairngorm download page 下載最新的 Cairngorm Binary file,想要了解整個架構的可以下載 Source file
以下是我練習的範例:by Flex Builder 3
目標:畫面上只有一個 list component 資料來源為外部 xml
menu tree :
list.xml :
1. 從 ModelLocator 開始寫 :
使用 Signleton design pattern 重點是將要使用的資料都收集在這邊,由於練習的範例只有顯示一個 list 所以只提供 一個 "list:ArrayCollection" 屬性,(如果複雜的可由 Model class 提供請參考 CairngormStoreWeb 範例寫法 )
ModelLocator 的用法大概就是:(理解錯誤的話請糾正喔!)
用來收集一堆 app 用的 Models 並提供單一個實體存取所有的資料。[Bindable] 是直接與顯示資料的 Viewer 綁定 (另類的 Observer 模式 ?),也讓 Cairngorm 架構內的 ViewHelper and ViewLocator patterns 變的可有可無 。David Tucker 在文章最後提了幾個重點很有用,其中兩項:ModelLocator 的資料只給 Command or Responder layer 編輯,清楚的釐清工作分責再來就是 不要使用 ViewHelper and ViewLocator (我實在搞不清楚它們的使用意義 ? )。
2. listVO :
3. GetListCommand :
Cairngorm Event 傳遞機制就是:
在 FrontComtroller 註冊 Commond 與對應的 Event
-> CairngormEventDispatcher 監聽 Event,爾後操作過程中 CairngormEventDispatcher dispatch Event
-> 實體化相關 Commond 看是編輯 ModelLocator 或者透過 Data Delegate 對外連線並回傳到對應的 Responder 修改 ModelLocator
-> Viewer 更新顯示
感覺起來有點複雜,但是實際走過一遍後會發現它分工很明確,正因為太清楚...感覺開發到最後 Commond 如果沒有好好規劃的話 Events and Commonds 可能會滿天飛,檔案越多當然就代表 debug 越麻煩啦!
4. ListDelegate :
對外部資料的管道,重點是將 server side 的 data 轉換成 application 使用的 data 型態回覆給 Responder ,使用它的好處是:更換 web services 時只需要換掉 data delegate 與修改 Service.mxml 即可。
5. Service.mxml :
使用 mxml 來寫超方便的啦!
6. PageController :
註冊 Commond 到 FrontController
7. 完成 CairngormTest.mxml
整個練習完後至少對 Cairngorm 有點概念,當然要熟練的實做還需要一些時間的努力啊!=P
先到 Adobe open source Cairngorm download page 下載最新的 Cairngorm Binary file,想要了解整個架構的可以下載 Source file
以下是我練習的範例:by Flex Builder 3
目標:畫面上只有一個 list component 資料來源為外部 xml
menu tree :
list.xml :
<?xml version="1.0" encoding="utf-8"?>
<data>
<list name="AAAA" data="0"/>
<list name="BBBB" data="1"/>
<list name="CCCC" data="2"/>
<list name="DDDD" data="3"/>
</data>
1. 從 ModelLocator 開始寫 :
使用 Signleton design pattern 重點是將要使用的資料都收集在這邊,由於練習的範例只有顯示一個 list 所以只提供 一個 "list:ArrayCollection" 屬性,(如果複雜的可由 Model class 提供請參考 CairngormStoreWeb 範例寫法 )
package com.model
{
import com.adobe.cairngorm.model.ModelLocator;
import mx.collections.ArrayCollection;
[Bindable]
public class TestModelLocator implements ModelLocator
{
private static var instance:TestModelLocator = null;
private static var allowInstantiation:Boolean = false;
public var list:ArrayCollection;
public function TestModelLocator(){
if (!allowInstantiation) {
throw new Error("Error: Instantiation failed: Use TestModelLocator.getInstance() instead of new TestModelLocator().");
}
list = new ArrayCollection;
}
public static function getInstance() : TestModelLocator {
if ( instance == null ) {
allowInstantiation = true;
instance = new TestModelLocator();
allowInstantiation = false;
}
return instance;
}
}
}
ModelLocator 的用法大概就是:(理解錯誤的話請糾正喔!)
用來收集一堆 app 用的 Models 並提供單一個實體存取所有的資料。[Bindable] 是直接與顯示資料的 Viewer 綁定 (另類的 Observer 模式 ?),也讓 Cairngorm 架構內的 ViewHelper and ViewLocator patterns 變的可有可無 。David Tucker 在文章最後提了幾個重點很有用,其中兩項:ModelLocator 的資料只給 Command or Responder layer 編輯,清楚的釐清工作分責再來就是 不要使用 ViewHelper and ViewLocator (我實在搞不清楚它們的使用意義 ? )。
2. listVO :
package com.vo
{
import com.adobe.cairngorm.vo.IValueObject;
public class listVO implements IValueObject
{
public function listVO(label:String, data:String){
this.label = label;
this.data = data;
}
public var label:String;
public var data:String;
}
}
3. GetListCommand :
Cairngorm Event 傳遞機制就是:
在 FrontComtroller 註冊 Commond 與對應的 Event
-> CairngormEventDispatcher 監聽 Event,爾後操作過程中 CairngormEventDispatcher dispatch Event
-> 實體化相關 Commond 看是編輯 ModelLocator 或者透過 Data Delegate 對外連線並回傳到對應的 Responder 修改 ModelLocator
-> Viewer 更新顯示
感覺起來有點複雜,但是實際走過一遍後會發現它分工很明確,正因為太清楚...感覺開發到最後 Commond 如果沒有好好規劃的話 Events and Commonds 可能會滿天飛,檔案越多當然就代表 debug 越麻煩啦!
package com.commands
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.bussiness.ListDelegate;
import com.model.TestModelLocator;
import mx.collections.ArrayCollection;
import mx.rpc.IResponder;
public class GetListCommand implements ICommand, IResponder {
private var modelLocator:TestModelLocator = TestModelLocator.getInstance();
public function GetListCommand() {}
public function execute(event:CairngormEvent):void {
//因為要取用外部資料所以需要 Data delegate來輔助
//Commond 都不用管 Delegate 如何取得資料
var delegate:ListDelegate = new ListDelegate( this );
delegate.getList();
}
public function result( event:Object ):void {
//會在 Delegate Layer 中將 result 處理成 ArrayCollection 型態
modelLocator.list = event.result as ArrayCollection;
}
public function fault( event:Object ):void {
//------
}
}
}
4. ListDelegate :
對外部資料的管道,重點是將 server side 的 data 轉換成 application 使用的 data 型態回覆給 Responder ,使用它的好處是:更換 web services 時只需要換掉 data delegate 與修改 Service.mxml 即可。
package com.bussiness {
import com.adobe.cairngorm.business.ServiceLocator;
import com.vo.listVO;
import mx.collections.ArrayCollection;
import mx.rpc.IResponder;
import mx.rpc.events.ResultEvent;
public class ListDelegate {
private var responder:IResponder;
private var service:Object;
public function ListDelegate( responder:IResponder ) {
//responder = GetListCommand
this.responder = responder;
//service 的名稱要看 service.mxml 中設定的 service id
this.service = ServiceLocator.getInstance().getHTTPService( "GetListService" );
}
public function getList():void {
var call:Object = service.send();
/*可以將 service side data 保留在這層處理與 Commond 切分清楚 */
var responder:mx.rpc.Responder = new mx.rpc.Responder(onResult, onFault);
call.addResponder(responder);
}
private function onResult( event:ResultEvent ):void {
var xml:XML = XML(event.result);
var list:ArrayCollection = new ArrayCollection;
for each (var subxml:XML in xml.list){
list.addItem( new listVO ( subxml.@name, subxml.@data));
}
//處理完後送回給 ListCommand
//如果取用 list.xml 的方法轉成 remoting or webService 的話,也只是需要換掉這個 Class
responder.result( new ResultEvent( ResultEvent.RESULT, false, true, list ) );
}
private function onFault( event:Object ):void {
trace(" 載入 list.xml 失敗~~~");
}
}
}
5. Service.mxml :
使用 mxml 來寫超方便的啦!
<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<mx:HTTPService
url="data/list.xml"
id="GetListService"
resultFormat="e4x"
/>
</cairngorm:ServiceLocator >
6. PageController :
註冊 Commond 到 FrontController
package com.control
{
import com.adobe.cairngorm.control.FrontController;
import com.commands.GetListCommand;
public class PageController extends FrontController
{
public function PageController()
{
super();
//加入 Command ~~
this.addCommand( "getList", GetListCommand );
}
}
}
7. 完成 CairngormTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" applicationComplete="init()">
<mx:Script>
<![CDATA[
import com.control.PageController;
import com.model.TestModelLocator;
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.control.CairngormEventDispatcher;
import com.bussiness.Service;
private var service:Service, controller:PageController;
[Bindable]
private var model:TestModelLocator = TestModelLocator.getInstance();
private function init():void{
//實體化 PageController and Service
controller = new PageController;
service = new Service;
//取 list 資料
CairngormEventDispatcher.getInstance().dispatchEvent(new CairngormEvent("getList"));
}
]]>
</mx:Script>
<mx:List dataProvider="{model.list}" />
</mx:Application>
整個練習完後至少對 Cairngorm 有點概念,當然要熟練的實做還需要一些時間的努力啊!=P
這篇實在太好用了....正好在看Cairngorm,看到有一點點想抓狂,Erin這篇超清楚的!Thanks!! :D
ReplyDeleteViewHelper的作用就是用来做一些画面的辅助工作的。当画面上会有比较复杂的逻辑需要处理的时候,虽然可以直接写到MXML文件里面,但是那样看起来会非常乱,所以一般是把那些逻辑单独地写到一个ViewHelper里面。
ReplyDelete謝謝說明~~^^ 意思就是如果我需要讓 UI component 更單純的話就需要使用 ViewHelper 囉...
ReplyDelete