2011-05-06

Parsley - An AS3 Framework

目前 AS3 比較熱門的 Framework 有:PureMVC, Robotlegs, ... 等等。

Adobe 官方也有一套 Cairngorm,不過它自從 2007 年後就停止更新了。

之前因為專案的關係而有接觸過。因為當初 AS3 Framework 的選擇並不多(應該說雖然有很多冒出頭,但都還在 alpha, beta 階段,或是根本很少人用),加上 Cairngorm 有著官方的加持,所以當初就選擇它來使用。但是用過的感覺並不是很好,覺得是個很「囉唆」的Framework(難怪後來就沒有更新了)。
之後的專案我就改用了當時的第二個選項:PureMVC。用了之後就覺得這 Framework 太妙了!沒有之前使用 Caringrom 的沈重感。而且更妙的是它有 port 到其他語言上,所以有次 C# 的專案我也拿 PureMVC 來練練身體 :D

某天我心血來潮,想看看很久沒動的 Cairngrom 目前狀況如何(有種想要窺探初戀情人現在如何的心情),才知道它還活著但是不再發展了。取而代之的是完全以「開發指導」自居的 Cairngorm 3

當然本篇的重點不是 Cairngorm,不然看倌們一定會以為我打錯標題了。關於 Cairngrom 3 的一些細節,這邊就不提了,請各位移駕到這裡參詳。

之所以會提到 Cairngorm ,是當我在瀏覽 Cairngorm 3 的時候,發現它提供了許多工具都是針對 Parsley,這就我讓我很好奇這是一個甚麼樣的 Framework。所以我 google 了一下然後到官方網站看看文件(題外話,原來 parsley 的翻譯有這麼多的分歧!?),整理一下心得分享給大家囉!


Parsley 是一套使用 AS3 撰寫的 Framework,可以使用它來開發 Flash/Flex/AIR 應用程式,有幾個比較重要的特點是:
  • IoC Container: 可以透過 AS3 Metadata, MXML, MXL, ActionScript 來設定
  • Dependency Injection: 可以過物件型別或是 id 來作建構式注入、方法注入、屬性注入
  • Messaging Framework: 完全解耦(Fully decoupled) 的發送端跟接收端

IoC Container

有關 IoC 的部份可以參考這個這個

Parsley 需要一個設定檔來設定要做 Dependency Injection 的物件。通常我會將之命名為: Context。
大概長得像這樣:


<Objects 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:services="com.bookstore.services.*"
    xmlns:actions="com.bookstore.actions.*"
    xmlns="http://www.spicefactory.org/parsley"
    >
        
    <fx:Declarations>
    
        <services:LoginServiceImpl timeout="3000"/>

        <actions:LoginAction/>
        
    </fx:Declarations>

    
</Objects> 



透過這個設定檔,Parsley 就會知道哪些物件要做 Dependency Injection 。除了使用 MXML 之外,也可以使用 XML 或是直接用 AS 來寫。

Dependency Injection

Parsley 可以透過 [Inject] 這個 Metadata 標籤來指派那個物件的屬性是要被注入的。預設會以物件的型別去做注入,也可以加上 id 來作注入。

class LoginServiceImpl {

  [Inject]
  public var service:RemoteObject;

  // Using the object as usual
public funciton login():void {
    service.login();
//...

}
需注意到的是 service 屬性必須為 public 否則 Parsley 無法作注入的動作。
service 這個變數不需要另外去作 new 的動作,他在 IoC 的容器下就會自動將先前設定好的物件注入到 service ,所以在 class 內的使用就如同一般的使用方式。

這邊有個小技巧,通常我會把 service 設計成一個 interface ,方便日後可以抽換。

Messaging Framework

這個是 Parsley 的一個重點。Parsley 提供了一個很棒的訊息溝通機制,讓物件很方便地可以彼此溝通,但卻又不必知道溝通的對象是誰。這個訊息溝通機制讓 Parsley 變得很迷人!

訊息可以是任何的物件(不一定要得是 Event,不過我習慣用 Event)。
如果是使用 Event 或是自訂的 Event ,只要利用 ManagedEvents 這個標籤設定好要管理的事件,當物件做了 dispatchEvent 時,Parsley 就會知道,其他的物件只要利用 MessageHandler, MassageBinding 這兩個標籤就可以接收訊息了!

[Event(type="getPictures", name="example.events.PictureEvent")]
[ManagedEvents("getPictures")]
class PicturePM extends EventDispatcher {
  public function getPictures():void {
    // 如同平常那樣發送事件 
dispatchEvent(new PictureEvent(PictureEvent.GET_ALL));
  }
 
// ...
}

如果是一般物件,做法會是這樣:

class PicturePM {
  [MessageDispatcher]
  public var dispatcher:Funciton;

  public funciton getPictures():void {
    dispatcher(new GetPicturesMessage());
  }
}

上面的 GetPictureMessage 就是自己定義的一般物件。


接收端的物件類似這樣:

class PictureService extends EventDispathcer {
// 設定接收自哪個事件類別以及事件名稱
  [MessageHandler(type="example.events.PictureEvent", selector="getAll")]
  public function getAllPics(event:PictureEvent):void {
    var e:PictureEvent = new PictureEvent(PictureEvent.GOT_ALL);
    e.pics = pics;
    //整理好後就一樣發送事件
    dispatchEvent(e);
  } 
}

在 PicturePM 類別內可以在新增一個 method 並加上 MessageHandler 來處理收到的事件。或是可以用更方便的 MessageBinding 標籤:

[MessageBinding(messageProperty="pics", type="example.events.PictureEvent", selector="gotAll")]
public var pics:Array;

使用了 MessageBinding 的物件屬性,就會在接受到指定事件時自動變更。所以當上面的 PictureService 處理好的 pics 之後,發送事件出去後,PicturePM 裡的 pics 屬性就會自動更新。真是太方便了!!

結論

最近用起來的感覺很不錯,在強大的訊息溝通機制下,不用做類似 PureMVC 那樣煩瑣的通知設定(在Facade 裡註冊相對應要執行的 command;mediator 裡要列出有興趣的通知,以便做相對應的動作),可以少寫很多程式碼。

搭配上 Dependency Injection ,整體的感覺很靈活,寫起來的感覺更是輕巧,味道很好!!

另外,Parsley 很徹底的貫徹 Decoupling 的想法,甚至希望除了每個 class 搞得互相不認識之外,也希望最好 class 也不要認識 Parsley 這個 framework(我很喜歡,總之就是最好搞得大家都不認識),以提高重用的機會,也方便做單元測試。所以它不會像其他的 framework 有設計好的架構,使用時就用繼承的方式來建構出你的應用程式。

因為它沒有提供任何架構(如MVC),所以好處是可以自己設計架構,當然反面就是如果沒有好一點的概念的話,還是會寫出一些奇怪的東西。


有興趣的話,大家可以去官網下載會來玩玩看,或是先看看手冊瞭解一下。

希望 Parsley 可以為你的 ActionSctipt 帶來不一樣的風味!

沒有留言: