Micro Architecture ─ Cairngorm

Cairngorm是Flex上的微架構,主要是幫助設計師在撰寫程式時,能有效把專案分成Model、View、Controller三種架構,方便日後的維護與修改,這種架構比傳統寫法(只使用類別或介面做分類)來的複雜,開發時間也較長,但對長久經營的軟體來說,這種分法絕對有利,畢竟大型專案愈到後面會愈複雜,MVC的架構在維護跟分工上都比較容易。

Cairngorm是以Flex為基礎的架構,顧名思義,只能在Flex使用,因為Cairngorm用到了很多只有Flex才有的特性,像是Data Binding、States,雖然很方便但這也限制了程式的發展,如果你想用非Flex的元件,那只能再包一層Component,並使用Binding Util來符合原本的架構,這就失去了原本Flex開發的好處,不過Cairngorm是Adobe官方認可架構,在純Flex開發上還是有他的優勢存在,而且很簡單,如果是初試MVC的新手,可以先學學看Cairngorm,了解一下MVC的好處,之後可在嘗試其它的Framework(像是pureMVC、Mate、Swize)。

Cairngorm大至上可分成FrontController、ModelLocator、ServiceLocator及View跟Event這五類

  1. FrontController是管理Event觸發時所要執行的Command,Command可存取ModelLocator或向遠端要值
  2. ModelLocator為Singleton,定義了所有用到的資料,所有的View都能以Binding的方式向此要資料
  3. ServiceLocator同樣為Singleton,定義了遠端溝通的方法,並藉由Delegate的方式向遠端要值
  4. View為所有用到的可視元件,可觸發Event
  5. Event定義了事件的類形及傳送值

Adobe Opensource Cairngorm http://opensource.adobe.com/wiki/display/cairngorm/Cairngorm

做個簡單範例,先看一下類別圖

2009-03-09_215532.jpg

程式入口,先生成所有物件,除了TestModelLocator外其它都以mxml撰寫



<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
initialize="init();"
xmlns:control="idv.gd.control.*"
xmlns:views="idv.gd.views.*"
xmlns:business="idv.gd.business.*" >

<mx:Script>
<![CDATA[
import idv.gd.model.TestModelLocator;

private function init():void
{
//一開始生成本地資料,讀入LSO
TestModelLocator.getInstance();
}
]]>
</mx:Script>
<control:Controller id="controler" />
<views:BasicView />
<business:Services />
</mx:Application>

在Controller我定義了那些Event要做那些Command



package idv.gd.control
{
import com.adobe.cairngorm.control.FrontController;
import com.adobe.cairngorm.control.CairngormEventDispatcher;
import idv.gd.events.SubmitEvent;
import idv.gd.events.FormEvent;
import idv.gd.commands.LoginCommand;
import idv.gd.commands.FormChangeCommand;
import idv.gd.commands.RegisterCommand;

public class Controller extends FrontController
{
public function Controller()
{
this.addCommand(SubmitEvent.LOGIN_SUBMIT, LoginCommand);
this.addCommand(SubmitEvent.REGISTER_SUBMIT, RegisterCommand);

this.addCommand(FormEvent.REGISTER, FormChangeCommand);
this.addCommand(FormEvent.LOGIN, FormChangeCommand);
}
}
}

在Command內,我們可以操作Model跟透過Delegate向Server要值,比較特殊的是我把RegisterCommand 實作了IResponder,讓Delegate直接送回傳值給Command



public class RegisterCommand implements ICommand, IResponder
{
private var model:TestModelLocator = TestModelLocator.getInstance();

public function execute(event:CairngormEvent):void
{
var submitEvent:SubmitEvent = SubmitEvent(event);

var delegate:ServerDelegate = new ServerDelegate(this);

model.formEnabled = false;
model.msg = "讀取資料中";

delegate.getHSData();
}

//用來接受從serverlocator回傳的訊息
public function result(event:Object):void
{
//trace(event);
model.formEnabled = true;
model.msg = "註冊成功 msg:" + event.result;
}

public function fault(event:Object):void
{
//trace(event);
model.formEnabled = true;
model.msg = "註冊失敗 msg:" + event.message;
}
}

使用Delegate透過Servers的serviceHS收發資料,並傳回給Command(Responder),Services記得在一開始時要先生成



package idv.gd.business
{
import com.adobe.cairngorm.business.ServiceLocator;

import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;

/**
* ServiceDelegate為伺服器資料的代理,透過CairngormCommand存取位置網路端的資料
* 本身要能處理伺服器端的反應(result, fault),整理結果並回傳給CairngormCommand
*/
public class ServerDelegate
{
private var commandResponder:IResponder;

/**
* 接收responder
* @param responder 用來觸發反應的responder物件,一般為CairngormCommand
*/
public function ServerDelegate(responder:IResponder)
{
commandResponder = responder;
}

/**
* 透過Service.mxml生成與伺服器溝通的HTTPService物件
* 利用asyncToken接收非同步訊息
*/
public function getHSData():void
{
var delegateService:HTTPService = ServiceLocator.getInstance().getHTTPService("serviceHS");
var asyncToken:AsyncToken = delegateService.send();

asyncToken.addResponder(commandResponder);
}
}
}

View以DataBinding方式挷定State跟Model



<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="374" height="220"
creationComplete="init();"
currentState="{model.currentState}"
enabled="{model.formEnabled}"
backgroundColor="#FFFFFF">



[Bindable]
private var model:TestModelLocator = TestModelLocator.getInstance();

Model實際上只存值,不做事件的發送(DataBinding己做掉了),可以視情況使用getter跟setter



package idv.gd.model
{
import com.adobe.cairngorm.model.IModelLocator;
import flash.net.SharedObject;

[Bindable]
public class TestModelLocator implements IModelLocator
{
public var userName:String = "";
public var userPWD:String = "";
public var currentState:String = "";
public var msg:String = "請輸入使用者帳號";

public var isRemember:Boolean = false;
public var formEnabled:Boolean = true;

public var LSOName:String = "userInfo";

private static var instance:TestModelLocator = null;

/**
* 第一次生成時讀入LSO
*/
public function TestModelLocator(model:SingleModel)
{
var lso:SharedObject = SharedObject.getLocal(LSOName);

if (lso.data.isRemember)
{
userName = lso.data.userName;
userPWD = lso.data.userPWD;
isRemember = lso.data.isRemember;
}
}

public static function getInstance():TestModelLocator
{
if (instance == null)
instance = new TestModelLocator(new SingleModel());

return instance;
}
}
}
//for singleton
class SingleModel
{
public function SingleModel()
{
}
}

0 意見 :: Micro Architecture ─ Cairngorm