ROBOTLEGS輕量級AS3框架

Robotlegs是一個用來開發FlashFlexAIR應用的純AS3微架構(框架)Robotlegs專注於將應用程序各層排布在一起並提供它們相互通訊的機制。Robotlegs試圖通過提供一種解決常見開發問題的經過時間檢驗的架構解決方案來加速開發。Robotlegs無意鎖定你到框架,你的類就是你的類的樣子,而且應該很容易地切換到其他框架。

框架提供一個基於Model-View-Controller元設計模式的默認實現。這個實現提供一個針對應用程序結構和設計的強烈建議。雖然它確實輕微減低了你的應用程序的便攜性,不過它依然以最低限度影響你的具體類爲目標。通過擴展MVCS實現類,你可以獲得很多有用的方法和屬性。

你不必使用Robotlegs的標準MVCS實現。你可以使用它的任意部分,或者完全不使用它,或者使用自己的實現來適應你的需求。它是爲了提供合適的參考實現和快速開始使用Robotlegs而被包含進來。

目錄

1.      MVC

1.1.       MVC分解

1.2.       消息通信、依賴注入等

2.      ROBOTLEGS與其它框架的比較

3.      ROBOTLEGS的依賴注入

3.1.       依賴注入

3.2.       SwiftSuspenders

SwiftSuspenders 適配器注入語法

4.      ROBOTLEGS的標準MVCS實現

4.1.       Robotlegs MVC+S

4.2.       推薦目錄結構

4.3.       Context

提供根視圖(root-view

調用startup()方法

4.4.       Command

Command 職責

觸發 Command

應用程序層的解耦

4.5.       View & Mediator

Mediator 職責

映射一個 Mediator

訪問一個 Mediator  View Component

給一個 Mediator 添加事件監聽

監聽框架事件

廣播框架事件

監聽 View Component 事件

通過 Mediator 訪問 Model  Service

4.6.       Models

Model 職責

映射一個 Model

從一個Model裏廣播事件

4.7.       Services

Service 職責

映射一個 Service

廣播框架事件

在一個 Service 裏解析數據

Service 事件

5.      擴展點

參考文獻

 

 

1.  MVC

clip_image002

MVC就是(Model View Controller)模型-視圖-控制器,是一個設計模式(也可以稱爲架構模式),但目前在大部分人眼裏顯然並不只有這點意義。受到各種MVC框架的影響,消息通信,依賴注入,隔離編譯等等概念現在都加入到了MVC的概念裏,而且因爲應用上的重疊和相似性,還常常和三層架構的概念混淆。

“一千個人眼中有一千個哈姆雷特”

一個名詞的意義,是由人們的理解來決定的,因此這裏從最基本的概念入手。

1.1.     MVC分解

對於一個用於顯示的程序,最開始,只有View(視圖)。

我們要獲得一個數據並顯示,就是寫段代碼調用接口,獲得數據,然後根據數據更新視圖。而這裏的數據只是一個臨時變量,或者存在於視圖的某個屬性中。顯然,這樣做的話,數據是依賴於視圖的,如果不通過視圖,或者視圖根本不存在的時候,其他模塊就訪問不到這個已經返回的數據,則需要重新調用一次,這顯然是一種浪費,而且也不便利。因此我們在這裏將數據保存到別處,和視圖不共享同一生命週期,那麼這分離出來的數據則被規定爲Model(模型)。

(在這裏我必須打斷說明下,模型!=數據,僅僅是在這個簡化例子裏模型==數據。模型是爲視圖提供內容的單獨模塊,具體內部是什麼情況則是各種各樣的,提供數據只是它的功能之一)

至此,就是最基本的分離表現層的原則。然而,有個地方卻是分不開的——原來控制加載數據的這部分代碼。它負責的是獲取數據,並指示視圖根據數據更新的任務,而它的去向有兩個選擇:要不加到視圖上(這是通常的做法),要不加在模型上(如果需要模型對應多個視圖),反正他不可能因爲分離就憑空消失。那麼我們把這部分代碼提出來作爲單獨的部分,就是Controller(控制器)。

一旦分離出了控制器,聯繫模型和視圖的具體代碼就不再處於兩者之上(當然調用代碼還是有的),而是一個獨立的部分。它有自己的名字,而且不會和其他不相關的內容混合在一起,非常清晰,容易定位。而模型和視圖從此也只關心控制器,而不關心對方。他們的代碼都是處理自己的事情,別人的事情全部交給控制器去辦,這使得他們自己的功能也非常獨立,減少了需要考慮的要素。

這只是代碼位置的移動,但是這樣移動後,將牽涉內容最多最易變,但是代碼量最少的代碼單獨放入控制器中,並妥善管理。相當與將分散在房間各處的開關集中於一處,排列整齊並標註名字,做成遙控器在手裏把玩,從此就能從奔波於房間各個位置,尋找隱藏在角落裏的小突起的日常活動中解放出來。其意義,不言而喻。

而這,作爲MVC的作用,已經足夠了。

1.2.     消息通信、依賴注入等

一旦我們將視圖,模型,控制器分離後,可以做到什麼呢?

因爲視圖和模型都只用處理自己的事情,對控制器的調用只是一句代碼而已,那麼,它們實際上就可以被隔離出去。負責這部分代碼的人只需要關心自己的事情,而不需要關心整個環境。同樣的,編寫控制器的人只需要知道視圖和模型的對外接口,而不需要了解它們的具體實現,也就是只需要關心環境。這使得不同開發者需要的知識被分隔開,每人只需要瞭解有限的少量知識,最終卻又能順利合併在一起。

這是就是協作分工的基礎。

在這之後,三者的關係只存在簡單的調用代碼。那麼爲了能夠徹底的分離和解耦,就可以將調用代碼改爲發送消息或者其他的動態形式,這樣就能在沒有其他部分的時候獨立編譯。由不同人在不同的工作環境獨立完成自己的部分,並在最後發佈時候簡單合併在一起,這確實是最理想的協作模式。

做到這點需要一個消息框架,而這就是MVC框架的主要任務。除此之外,各種不同的框架還會加入其它設計模式,提供各種附加功能,自動依賴注入就是其中一項。還可能加入其它的中間件,進一步分割層次,諸如分離出視圖其中的邏輯部分,使得繪圖和位置代碼不會和邏輯代碼混合,方便分工以及修改。使用觀察者模式,使得數據部分的修改可以自動同步到視圖上,如此這般……MVC框架指的是“實現MVC的框架”,而非“只實現MVC的框架”,僅僅實現MVC,這個框架的功能太過貧乏了,畢竟MVC也就是那種程度的東西。

最終,完成了這些之後,就成爲了一般人表面看到的東西。雖然各式各樣,我們都將其稱之爲“MVC框架”。

2.  RobotLegs與其它框架的比較

下面對比下比較熱門的幾個as3框架:

Framework

Dependencies Management

Event Management

Presentation Pattern

Cairngorm

Singleton

Singleton Dispatcher

Code Behind

PureMVC

Service Locator

Notification

Mediator

Mate

Dependency Injection

Display list

Presentation Model

Swiz

Dependency Injection

Display list

Presentation Model

Parsley

Dependency Injection

Central Dispatcher

Presentation Model

Robotlegs

Dependency Injection

Event bus

Mediator or others

pureMVCCairngorm是兩個較早出現的框架,目前我不建議再使用它們。pureMVC的問題在於過於強調分離而缺乏實際功能,提供的便利很難抵消它本身的消耗,性價比較低。Cairngorm的問題則在於過於強調模型更新視圖的流程,限制太多,靈活程度不夠。

後出的幾個框架就好多了,Mate使用了一個全局事件定義,配合FLEX寫法非常簡略。Swiz則是用控制反轉+依賴注入,也就是Spring的做法,而且元標籤注入的方式很有趣,感興趣的可自行查閱資料。

RobotLegs是一個和Swiz非常相似的框架,但也有一些自己的特點。RobotLegs它是基於pureMVC的,可以像pureMVC這樣來使用它,對於使用pureMVC的團隊它是很容易接受的代替品。pureMVC是基於Notification的一個MVC框架,主要目的是爲了各個部分能夠解耦,當然它也基本上能夠做到。RobotLegs則是基於消息以及消息攜帶的數據等來實現解耦。RobotLegs是基於pureMVC的思想,但是在一些方面更加出色,例如消息的強類型依賴注入方式,消息攜帶數據等等。

robotlegs裏使用了flash的事件機制來通信,而puremvc使用自定的通知來發消息。這裏區別不大,只是使用事件機制就得寫事件類;然後robotlegs使用自動mediator自動註冊,它靠偵聽addtostage來處理,當然,手動註冊也是允許的。這樣方便了不少,puremvc只能手動在視圖組件初始化時註冊,而且有時有些內部組件經常會出現未初始化完成時就去註冊,導致訪問不到this。還有最重要的依賴注入,robotleg不再使用puremvc那樣的傳遞參數方法,而是使用依賴注入,包括mediatorview組件的引用都是注入的。這樣依賴性又小了很多,感覺非常不錯。

個人認爲RobotlegspureMVC好用,RobotlegspureMVC的改進升級版而且是專注於as3的,注入技術更是省去了pureMVC的許多麻煩。

3.  RobotLegs的依賴注入

RobotLegs使用了以下3個面向對象設計模式:

ü  自動依賴注入(Automated Dependency Injection,不用對象自己創建、獲取依賴的對象,而是由容器/框架來完成。

ü  命令模式(Command Pattern

n  命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開;

n  每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作。

n  命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。

ü  調度者模式(Mediator Pattern,定義一箇中介對象來封裝系列對象之間的交互。中介者使各個對象不需要顯示地相互引用,從而使其耦合性鬆散,而且可以獨立地改變他們之間的交互。

Robotlegs圍繞依賴注入設計模式展開。最簡單地,依賴注入是爲對象提供實例變量或屬性的行爲。

當你傳遞一個變量到一個類的構造函數,你在使用依賴注入;

當你設置一個類的屬性,你在使用依賴注入;

如果你不是使用嚴格的過程或線性方式編寫AS3,很可能你現在就在使用依賴注入。

Robotlegs使用基於元數據的自動依賴注入。這是爲了方便開發而提供,而且在排布應用程序並提供類和它所需要的依賴時,可以減少很多代碼量。雖然完全可以手動提供這些依賴,但是允許框架來履行這些職責可以減少出錯的機會,並且通常可以加快編碼進程。

3.1.        依賴注入

依賴注入(Dependency Injection,簡稱DI),是一個重要的面向對象編程的法則來削減計算機程序的耦合問題。依賴注入還有一個名字叫做控制反轉(Inversion of Control,英文縮寫爲IoC)。依賴注入是這樣一個過程:由於某客戶類只依賴於服務類的一個接口,而不依賴於具體服務類,所以客戶類只定義一個注入點。在程序運行過程中,客戶類不直接實例化具體服務類實例,而是客戶類的運行上下文環境專門組件負責實例化服務類,然後將其注入到客戶類中,保證客戶類的正常運行。即對象在被創建的時候,由一個運行上下文環境或專門組件將其所依賴的服務類對象的引用傳遞給它。也可以說,依賴被注入到對象中。所以,控制反轉是,關於一個對象如何獲取他所依賴的對象的引用,這個責任的反轉

clip_image004

l  依賴注入使用以下三種基本技術:

n  類型1 (基於接口): 可服務的對象需要實現一個專門的接口,該接口提供了一個對象,可以從用這個對象查找依賴(其它服務)

n  類型2 (基於setter): 通過屬性的setter方法爲可服務對象指定服務。

n  類型3 (基於構造函數): 通過構造函數的參數爲可服務對象指定服務。

不要重複發明輪子!

對於應用頻繁的需求,總是有人設計各種通用框架和類庫以減輕人們的開發負擔。例如,數據持久化是非常頻繁的需求,於是各種ORM框架應運而生;再如,對MVC的需求催生了Struts等一批用來實現MVC的框架。

隨着依賴注入變成了非常頻繁的需求,而如果全部手工完成,不但負擔太重,而且還容易出錯。再加上反射機制的發明,於是,自然有人開始設計開發各種用於依賴注入的專用框架。這些專門用於實現依賴注入功能的組件或框架,就是IoC Container

IOC關注服務(或應用程序部件)如何定義的以及他們應該如何定位他們依賴的其它服務。所以通常,通過一個容器或定位框架(著名的Spring容器、)來獲得定義和定位的分離,容器或定位框架負責:

l  保存可用服務的集合

l  提供一種方式將各種部件與它們依賴的服務綁定在一起

l  爲應用程序代碼提供一種方式來請求已配置的對象(例如,一個所有依賴都滿足的對象), 這種方式可以確保該對象需要的所有相關的服務都可用。

 

3.2.        SwiftSuspenders

Robotlegs採用一種適配器(adapter)機制來爲框架提供依賴注入機制。默認地,框架配備了SwiftSuspenders注入/反射庫來適合這個要求。另有SmartyPants-IoC  Spring Actionscript 的適配器可以使用。可能有潛在的特定需求來使用其它的依賴注入適配器,但是如果沒有特別的理由,建議使用默認的SwiftSuspenders,因爲它爲 Robotlegs 做了一些特別調整。

clip_image006

SwiftSuspenders 適配器注入語法

SwiftSuspenders 支持三種類型的依賴注入:

ü  屬性()注入

ü  參數(方法/設值注入

ü  構造注入

我們將特別介紹屬性注入,以及在 Robotlegs 裏如何使用。將屬性注入類有兩種選擇,你可以使用未命名,或命名的注入:

[Inject]

public var myDependencyDepedency; //未命名注入

[Inject(name="myNamedDependency")]

public var myNamedDependencyNamedDepedency; //命名注入

Robotlegs裏三處提供了注入映射:MediatorMapCommandMap、直接通過InjectorMediatorMapCommandMap也都是使用Injector,但它們同時做了一些各自層(tier)所需要的額外工作。顧名思義,MediatorMap用來映射MediatorCommandMap用來映射Command;其它所有需要被注入的內容 (包括但不限於 Model) 都要直接使用 Injector 映射。

Injector 類的映射注入

具體 Injector 類的適配器都遵照 IInjector 接口。 這個接口爲不同的依賴注入解決方案提供了統一的API 本文專注於 SwiftSuspenders,但這些語法同樣適應於其它任何遵照 Iinjector 接口的 Injector

Injector是你應用程序裏所發生的所有依賴注入的生產車間。它用來注入框架 actor,同時也可以用來執行你的應用程序所需要的任何其它注入。這包括但不限於 RemoteObjectsHTTPServices,工廠類,或者事實上任何有可能成爲你的對象所需要的依賴的類/接口。

下面是實現 IInjector 接口的類所提供的四個映射方法:

clip_image008

MediatorMap 類的依賴注入

MediatorMap 實現 IMediatorMap 接口。 IMediatorMap 提供兩個方法來將你的 mediators 映射到 view 並註冊它們以便用來注入。

mapView(viewClassOrName*mediatorClassClassinjectViewAsClass = nullautoCreateBoolean = trueautoRemoveBoolean = true)void

mapView 接受一個視圖類,MyAwesomeWidget,或者一個視圖的類全名,com.me.app.view.components::MyAwesomeWidget 作爲第一個參數。 第二個參數是將要作爲視圖組件中介的 Mediator 類。 [injectViewAs 內容未完成],最後的兩個參數 autoCreate  autoRemove 提供方便的自動管理 mediator 的布爾值開關。

//在你的程序裏某個映射/配置發生的地方

mediatorMap.mapView(MyAwesomeWidgetMyAwesomeWidgetMediator);

//conntextView 的顯示列表裏的某個地方

var myAwesomeWidget:MyAwesomeWidget = new MyAwesomeWidget();

this.addChild(myAwesomeWidget); // ADDED_TO_STAGE 事件被拋出,觸發這個視圖組件的中介機制

這個方法使用了自動中介機制。

CommandMap 類的依賴注入

CommandMap 類實現 ICommandMap 接口,提供一個用來將 command 映射到到觸發它們的框架事件的方法。

mapEvent(eventTypeStringcommandClassClasseventClassClass = nulloneshotBoolean = false)

你要提供給 commandMap 一個可以執行的類,執行它的事件類型,可選的這個事件的強類型,以及這個 command 是否只被執行一次並且隨即取消映射的布爾值開關。

這個可選的強類型事件類用來對Flash平臺的"magic string"事件類型系統做額外的保護。以避免採用相同事件類型字符串的不同事件類之間的衝突。

//在你的程序裏某個映射/配置發生的地方

commandMap.mapEvent(MyAppDataEventDATA_WAS_RECEIVEDMyCoolCommandMyAppDataEvent);

//在事件被廣播的另外一個框架actor

//這觸發了隨後被執行的被映射的 command

dispatch(new MyAppDataEvent(MyAppDataEvent.DATA_WAS_RECEIVEDsomeTypedPayload))

4.  Robotlegs的標準MVCS實現

Robotlegs提供了一個比較規範的MVC+SModel模型,View視圖,Controller控件和Service服務)實現,來幫助開展工作。通過將幾個經過時間檢驗的設計模式整合到一個具體實現, Robotlegs  MVCS 實現可以用做創建你的應用程序的一致方案。 通過這些架構概念着手一個應用程序, 你甚至可以在開始你的設計之前避免很多常見的障礙:

ü  分離:MVCS 提供一種將你的應用程序分離到提供特定功能的無關聯的層的很自然的方法。 view 層處理用戶交互。 model 層處理用戶創建的或從外部獲取的數據。 controller 提供一種封裝各層之間複雜交互的機制。 最後, service 層提供一種和外界(比如遠程服務 API 或文件系統)交互的獨立機制。

ü  組織:通過這種分離我們自然獲得一個組織水平。 每個項目都需要某個組織水平。 是的,有人可以把他們所有的類都扔到頂級包下完事,但即使是最小的項目這也是不可接受的。 當一個項目有了一定的規模就需要開始組織類文件的結構了。 當向同一個應用程序開發中增加團隊成員的時候問題就更加嚴重了。 RobotLegs  MVCS 實現爲項目描繪出一個分爲四層的優雅的組織結構。

ü  解耦:Robotlegs MVCS實現將應用程序解耦爲4層。 每層都與其它層隔離, 使分離類和組件分別測試變得非常容易。除了簡化測試進程, 通常也使類更具便攜性以在其它項目中使用。 比如, 一個連接到遠程 API  Service 類可能在多個項目中都很有用。 通過解耦這個類, 它可以不需重構便從一個項目轉移到另一箇中使用。

4.1.        Robotlegs MVC+S

一個典型的Robotlegs MVC+S應用程序包含以下幾個部分:

clip_image010

ü  Context:所謂Context(上下文),實際上是一套自展機制,用來初始化Robotlegs所使用的依賴注入以及各種核心工具。

ü  Commands:所謂Commands(命令),代表的是應用程序所能執行的獨立操作。通常,Commands(命令)會作爲對用戶操作的反應,但命令的作用並不僅限於此。

ü  Mediators:所謂Mediators(中介),是用來管理應用程序中的視圖組件與應用程序中的其它對象之間的信息交流。

ü  ModelModels(模型)中保存着數據信息,並且表現出應用程序當前的狀態。

ü  ServiceServices(服務),是應用程序與外界的接口。

下面用用互操作view到得到響應的一個流程圖:

clip_image012

 

4.2.        推薦目錄結構

Robotlegs代碼包結構組織:

[domain.lib]

    various utilities

[domain.project]

    [projectmodule]

        [model]

            [events]

            [vo]

            ProjectModuleStateModel

        [view]

            [events]

            [renderers]

            [skins]

            MyProjectModuleView

            MyProjectModuleViewMediator

        [controller]

            [startup]

            MyProjectModuleActionCommand

        [service]

            [helpers]

            MyProjectModuleService

            IProjectModuleService

        [signals]

    [projectmodule]

        [model]

            [events]

            [vo]

            ProjectModuleStateModel

        [view]

            [events]

            [renderers]

            [skins]

            MyProjectModuleView

            MyProjectModuleViewMediator

        [controller]

            [startup]

            MyProjectModuleActionCommand

        [service]

            [helpers]

            MyProjectModuleService

            IProjectModuleService

        [signals]

    

這是Joel Hooks建議的包結構。這是一個多模塊情況的包結構,如果是單模塊,可以少去projectmodule這一層。[signals]包不是必須的,除非你用到了Signals

把每個模塊要都用到的公共代碼放到[domain.lib]下。模塊和模塊內代碼保證獨立,不會相互使用(引用,實例化)。

StartupCommand內代碼建議以MVC結構分離。如果一個模塊中StartupCommnad內有過多代碼,建議把它拆分成ModelStartupCommandControllerStartupCommandViewStartupCommand,這樣職責更清晰。

[controller][view]中,如果有很多類,建議按照功能建立子目錄。

4.3.        Context

實際上是一套自展機制,用來初始化Robotlegs所使用的依賴注入以及各種核心工具,一個應用程序是可以有多個 context 的,這對想要加載外部模塊的應用程序很有用,因爲在一個 context 裏的 actor 只能在他們的 context 定義的範圍之內相互通訊,所以在一個模塊化的應用程序裏,不同context 之間的通訊是完全可能的。

每一個Robotlegs項目都以Context開始,只有Context實例化之後,Robotlegs才能啓動和運行。創建Context需要給提供一個根視圖並調用startup()方法。

提供根視圖(root-view

每個Rogotlegs應用程序需要一個根視圖——DisplayObjectContainter的一個實例。它將供mediatorMap使用,所以當子視view圖添加到根視圖時,對應的mediator會被創建並和view關聯起來。

調用startup()方法

啓動應用程序需要在所有配置都準備好之後,然後調用startup()方法來啓動。可以通過將autoStartup:Boolean=true,然後該方法會自動被調用;又或者將autoStartup:Boolean=false,然後手動調用startup()方法。

startup()方法中會初始化依賴注入規則!如果規則很少,可以全部寫在這個該方法中;否則,建議分離到不同的配置類中。

4.4.        Command

Controller 層由 Command 類體現(一個Command是一個簡明、單一目的的控制器controller對象)。Command是短生命週期的無狀態對象。它們在被實例化和執行之後立即釋放。Command 應該只在處理框架事件時被執行,而不應該被任何其他框架 actor 實例化或執行。

clip_image014

Command 職責

Command 用於應用程序各層之間相互通訊,也可能用來發送系統事件。這些系統事件既可能發動其它的 Command 也可能被一個 Mediator 接收,然後對一個 View Component 進行對應這個事件的工作。

Command  Context  CommandMap 註冊到 ContextCommandMap  Context  Command 類裏默認可用。Command 類被註冊到 Context 時接收4個參數一個事件類型響應這個事件時執行的 Command 可選的事件類一個是否該 Command 只被執行一次隨即被取消註冊而不響應後續事件觸發的一次性設置.

觸發 Command

Command  MediatorsServicesModels,和其它 Command 廣播的框架事件觸發。一個被映射的 command 在響應一個框架事件時被實例化,所有已被映射,並被 [Inject] 元數據標籤標記過的依賴都會被注入到這個 Command。另外,觸發這個 Command 的事件實例也會被注入。當這些依賴被注入完畢,Command 的執行方法會被自動調用,Command 便會進行它的工作。你不需要,而且不應該直接調用 execute() 方法。這是框架的工作。

應用程序層的解耦

Command 是解耦一個應用程序裏各個 actor 的非常有用的機制。因爲一個 Command 永遠不會被 MediatorModel 或者 Service 實例化或執行,這些類也就不會被耦合到 command,甚至都不知道 command 的存在.

爲了履行它們的職責,Command 可能:

ü  映射 MediatorModelService,或者 Context 裏的其它 Command

ü  廣播可能被 Mediator 接收或者觸發其它 Command 的事件.

ü  被注入ModelService,和Mediator 以直接進行工作.

需要注意的是,不建議在一個 Command 裏直接和 Mediator 交互。雖然這是可行的,但會將這個 Mediator 耦合到這個 Command。因爲 Mediator 不像Service  Model,它可以接受系統事件,更好的做法是讓 Command 廣播事件,然後讓需要響應這些事件的 Mediator 監聽它們。

 

4.5.        View & Mediator

View  Mediator 類體現。繼承 Mediator 的類管理應用程序中的 View Component 與應用程序中的其它對象之間的信息交流。一個Mediator 將會監聽框架事件和 View Component 事件,並在處理所負責的 View Component 發出的事件時發送框架事件。這樣開發者可以將應用程序特有的邏輯放到Mediator,而避免把 View Component 耦合到特定的應用程序。

注意,Mediator僅僅是框架和View Component之間的橋樑,這也是他唯一的職責。

clip_image016

Mediator 職責

一個 View Component 是任何的UI組件和/或它的子組件。一個 View Component 是已被封裝的,儘可能多地處理自己的狀態和操作。一個 View Component 提供一個包含了事件,簡單方法和屬性的APIMediators負責代表它所中介的View Component和框架交互。這包括監聽組件及其子組件的事件,調用其方法,和讀取/設置組件的屬性.

一個 Mediator 監聽它的 View Component 的事件,通過 View Component 暴露的 API 訪問其數據。一個 Mediators 通過響應其它框架 actor 的事件並對自己的 View Component 進行相應修改來代表它們。一個 Mediator 通過轉發 View Component 的事件或自己向框架廣播合適的事件來通知其它的框架 actor.

映射一個 Mediator

任何可以訪問到mediatorMap實例的類都可以映射 Mediator。這包括 MediatorContext,和 Command .

這是映射一個 mediator 的語法:

mediatorMap.mapView( ViewClassMediatorClassautoCreateautoRemove );

view添加到舞臺上時,Mediator會被自動創建並關聯起來。

clip_image018

view從舞臺上移除時,Mediator會被自動清除。

clip_image020

訪問一個 Mediator  View Component

當一個 View Component 在一個 Context  contextView 裏被添加到舞臺上的時候,它默認地會被根據 MediatorMap 做映射時的配置被自動關聯。在一個基本的 mediator 裏,viewComponent會被注入爲被中介的 view component。一個 Mediator viewComponent屬性是 Object 類型的。在大多數情況下,我們希望訪問一個強類型的對象以從中獲益。爲此目的,我們注入被中介的 view component 的強類型實例:

public class GalleryLabelMediator extends Mediator implements IMediator

{

      [Inject]

      public var myCustomComponent:MyCustomComponent;

            

      /**

      覆寫 onRegister 是添加此 Mediator 關心的任何系統或 View Component 事件的好機會.

      */

      override public function onRegister():void

      {

             //添加一個事件監聽器到 Context 來監聽框架事件

             eventMap.mapListener( eventDispatcherMyCustomEvent.DO_STUFFhandleDoStuff );

             //添加一個事件監聽器到被中介的 view component

             eventMap.mapListener( myCustomComponentMyCustomEvent.DID_SOME_STUFFhandleDidSomeStuff)

      }

     

      protected function handleDoStuff(event:MyCustomEvent):void

      {

             //把事件的強類型負載設置到 view component 的屬性。

             //View component 很可能基於這個新數據管理自己的狀態.

             myCustomComponent.aProperty = event.payload

      }

     

      protected function handleDidSomeStuff(event:MyCustomEvent):void

      {

             //把這個事件轉發到框架

             dispatch(event)

      }

}

通過這種方法我們現在可以很方便地訪問被中介的 view component 的公開屬性和方法.

給一個 Mediator 添加事件監聽

事件監聽器是 Mediator 的眼睛和鼻子。因爲框架內的所有通訊都通過原生的Flash事件,Mediator 可以通過添加事件監聽器來響應感興趣的事件。除了框架事件,Mediator同時監聽所中介的 view component 的事件.

通常在 Mediator  onRegister 方法裏添加事件監聽。在 Mediator 生命週期中的這個階段,它已經被註冊並且它的 view component 和其它依賴也都已被注入。具體的 Mediator 類必須覆寫 onRegister 方法。也可以在其它方法裏添加事件監聽,比如響應框架事件和 view component 事件的事件處理方法裏.

Mediators 裝備了一個有 mapListener() 方法的 EventMap。這個方法註冊每個被添加到 Mediator 的事件監聽,並且確保 mediator 被框架取消註冊時刪除這些事件監聽。Flash 裏刪除事件監聽是很重要的,因爲如果一個類裏添加了事件監聽而沒有刪除,Player將無法對此類進行運行時垃圾回收(GCGarbage Collection)。也可以使用傳統的 Flash 語法添加事件監聽器,但要注意也要手動把它們刪除。

clip_image022

監聽框架事件

所有框架裏的actor在實例化時都會被注入一個eventDispatcher屬性。這個eventDispatcher就是 Mediator 發送和接受框架事件的機制.

eventMap.mapListener(eventDispatcherSomeEvent.IT_IS_IMPORTANThandleFrameworkEvent)

通過此語法,一個 Mediator 現在監聽了SomeEvent.IT_IS_IMPORTANT事件並在handleFrameworkEvent方法裏處理它。

廣播框架事件

Mediator的一個很重要的職責就是向框架發送其它 actor 可能感興趣的事件。這些事件通常是在響應應用程序用戶和被中介的 view component 之間的一些交互時發出的。這裏同樣有一個可以減少發送事件到框架的代碼輸入的很有用的方法:

dispatch(new SomeEvent(SomeEvent.YOU_WILL_WANT_THISmyViewComponent.someData))

這個事件現在可以被其它 Mediator 接收或者執行一個 command 了。發出事件的 Mediator 並不關心其它的 actor 如何迴應這個事件,它只是簡單地廣播一條有事發生的消息。一個 mediator 也可以監聽自己發出的事件,然後據此作出迴應.

監聽 View Component 事件

Mediator 負責所中介的 view component 發出的事件。這可以是個獨立組件,比如 TextField 或者 Button,也可以是有嵌套層級的複雜組件。當 mediator收到 view component 發出的事件會使用指定的方法處理它。和框架事件一樣,EventMap  mapListener 方法是給一個 mediator 添加事件監聽的首選.

eventMap.mapListener(myMediatedViewComponentSomeEvent.USER_DID_SOMETHINGhandleUserDidSomethingEvent)

響應一個 view component 的事件時,一個 mediator 可能:

ü  考察事件的 負載payload   (如果有)數據

ü  考察 view component 的當前狀態

ü  view component 進行需要的工作

ü  發送系統事件以通知其它actor有事發生

通過 Mediator 訪問 Model  Service

你的 mediator 可以監聽 Service  Model 類派出的系統事件來提高鬆耦合性。通過監聽事件,你的 mediator 不需要關心事件來源,而只需直接使用事件攜帶的強類型的 負載payload 數據。因此,多個 mediator 可以監聽相同的事件然後根據所收到的數據調整自己的狀態.

在一個 mediator 裏直接訪問 service 可以提供很大便利而不會帶來嚴重的耦合性問題。一個 service 並不存儲數據,只是簡單地提供一個向外部service發送請求並接受響應的API。能夠直接訪問這個API可以避免在你的應用程序中增加不需要的 command 類來達到同樣目的。如果這個 service API 在很多mediator 中通過相同的方式訪問,將此行爲封裝到一個 command 裏有益於保持此行爲的一致性並減少對此 service 的反覆調用以及在你的 mediator 裏的直接訪問.

建議通過 model  service 實現的接口將 model  service 注入 mediator

訪問其它 Mediator

如同 Service  Model,在一個 Mediator 裏也可以注入和訪問其它的 Mediator。這種做法是 強烈不建議的 因爲這種緊耦合可以簡單地通過使用框架事件進行通訊而避免。

 

4.6.        Models

Model 類用來管理對應用程序的數據模型的訪問。Model 爲其它框架actor提供一個 API 來訪問,操作和更新應用程序數據。這個數據包括但不限於原生數據類型比如 StringArray,或者像 ArrayCollection 一樣的域特有對象或集合.

Model 有時被當做簡單的 Model 比如 UserModel,有時也被當做 Proxy 比如 UserProxy。在 Robotlegs 裏,這兩種命名都是用作相同的目的,爲應用程序數據提供一個 API。不管採用哪種命名 model 都繼承提供了核心框架依賴和一些有用方法的 Actor 基類。

Model 會在對數據模型進行某些工作之後發出事件通知。Model 通常具有極高的便攜性。

Model 職責

Model類封裝了應用程序數據模型併爲其提供一個 API。一個 Model 類是你的應用程序數據的看門人。應用程序裏的其它 actor 通過 Model 提供的API 請求數據。因爲數據是通過 Model 更新,Model 裝備了向框架廣播事件的機制以向其它 actor 通知數據模型的變化使它們得以據此調整自己的狀態.

除了控制對數據模型的訪問,Model 通常也被用來保證數據狀態的有效性。這包括對數據進行計算,或域特有邏輯的其它領域。Model 的這個職責非常重要。Model 是應用程序中最有潛力具有便攜性的層。通過把域邏輯放入 Model,以後的 model 實現就不再需要像把域邏輯放入 View  Controller層那樣重複這些相同的邏輯,作爲一個例子,你的 Model 裏可能執行購物車數據的計算。一個 Command 將會訪問這個方法,最終的計算結果將會被作爲被某個 Mediator 監聽的事件派發出去。這個 mediator 將會根據這個被更新的數據更新自己的 view component,應用程序的第一個迭代是個典型的Flex 程序。這個計算也很容易在一個 Mediator 甚至視圖裏進行。應用程序的第二個迭代是一個需要全新視圖元素的移動設備 Flash 應用程序。因爲這個邏輯在 Model 裏,所以可以很容易被兩個完全不同元素的視圖複用.

映射一個 Model

Injector 有幾個方法可以用來將你的 Model 類映射到你的框架actor。另外,這些方法事實上可以用來注入任何類到你的類裏.

將一個已存在的實例當做一個單例注入映射,使用下面的語法:

injector.mapValue(MyModelClassmyModelClassInstance)

爲每個注入映射一個類的新實例,使用下面的語法:

injector.mapClass(MyModelClassMyModelClass)

另外,這也可以用來使用被注入的實現某接口的合適的類來映射這個用來注入的接口.

injector.mapClass(IMyModelClassMyModelClass)

爲某個接口或類映射一個單例實例,使用下面的語法:

injector.mapSingleton(MyModelClassMyModelClass)

需要注意重要的一點,當提及上面的一個單例時,它並不是一個單例模式的單例。在這個 Context 之外並不強制它作爲一個單例。Injector 簡單地確保這個類的唯一一個實例被注入。這對處理你的應用程序數據模型的 Model 非常重要.

從一個Model裏廣播事件

Model 類提供一個方便的dispatch方法用來發送框架事件:

dispatch( new ImportantDataEvent(ImportantDataEvent.IMPORTANT_DATA_UPDATED))

有很多理由派發一個事件,包括但不限於:

ü  數據已被初始化並準備好被其它 actor 使用

ü  一些數據片被添加到 Model

ü  數據被從 Model 中刪除

ü  數據已改變或者更新

ü  數據相關的狀態已改變

在一個 Model 裏監聽框架事件

雖然技術上可能,但強烈不建議這樣做。不要這樣做。只是爲了說清楚:不要這樣做。如果你這樣做了,不要說你沒被警告過.

4.7.        Services

Service 用來訪問應用程序範圍之外的資源。這包括但當然不限於:

ü  web services

ü  文件系統

ü  數據庫

ü  RESTful APIs

ü  通過 localConnection 的其它 Flash 應用程序

Service 封裝了這些和外部實體的交互,並管理這個交互產生的 result fault 或其它事件.

你可能注意到 Service  Model 的基類非常相像。事實上,你可能注意到除了類名,它們其實是一樣的。那麼爲什麼用兩個類呢? Model  Service 類在一個應用程序裏有完全不同的職責。這些類的具體實現將不再相像。如果沒有這個分離,你將經常發現 Model 類在訪問外部服務。這讓 Model 有很多職責,訪問外部數據,解析結果,處理失敗,管理應用程序數據狀態,爲數據提供一個 API,爲外部服務提供一個 API,等等。通過分離這些層有助於緩解這個問題。

clip_image024

Service 職責

一個 Service 類爲你的應用程序提供一個和外部服務交互的 API。一個 service 類將連接外部服務並管理它收到的響應。Service 類通常是無狀態的實體。他們並不存儲從外部服務收到的數據,而是發送框架事件來讓合適的框架 actor 管理響應數據和失敗.

映射一個 Service

injector 的多個可用的方法可以用來映射你的 Service 類以注入你的其它框架 actor。另外,這些方法也可以用來注入事實上任何類到你的類裏。

將一個已存在的實例當做一個單例注入映射,使用下面的語法:

injector.mapValue(MyServiceClassmyServiceClassInstance)

爲每個注入映射一個類的新實例,使用下面的語法:

injector.mapClass(MyServiceClassMyServiceClass)

另外,這也可以用來使用被注入的實現某接口的合適的類來映射這個用來注入的接口.

injector.mapClass(IMyServiceClassMyServiceClass)

爲某個接口或類映射一個單例實例,使用下面的語法:

injector.mapSingleton(MyServiceClassMyServiceClass)

需要注意重要的一點,當提及上面的一個單例時,它並不是一個單例模式的單例。在這個 Context 之外並不強制它作爲一個單例。Injector 簡單地確保這個類的唯一一個實例被注入.

在一個 Service 裏監聽框架事件

雖然技術上可能,但 強烈不建議 這樣做。不要這樣做。只是爲了說清楚不要這樣做。如果你這樣做了,不要說你沒被警告過.

廣播框架事件

Service 類提供一個方便的dispatch方法用來發送框架事件:

dispatch( new ImportantServiceEvent(ImportantServiceEvent.IMPORTANT_SERVICE_EVENT))

在一個 Service 裏解析數據

從外部獲取到的數據並不符合我們應用程序的 context。它們是外來者。可以圍繞外部數據類型對應用程序進行建模,或者更可取地,轉換這些數據以符合應用程序。應用程序裏有兩處可以進行這項操作/轉換。Service  Model 都很適合。Service 是進入外部數據的第一個點,所以它是操作一個外部服務返回的數據的更好的選擇。外來數據應該在第一個機會轉換到應用程序域。

提供一個使用工廠類而不是在 service 裏生成應用程序域對象的例子… 適當的

當數據被轉換爲應用程序域特有的對象之後發出帶有強類型負載的事件以被對此關心的 actor 立即使用.

Service 事件

service 組合的最後一個部分是自定義事件。沒有事件的 service 只是啞巴。他們可能做的任何工作都不會被其它框架成員注意到。一個 service 將會使用自定義事件來嚮應用程序發出聲音。事件並不一定是唯一的意圖。如果這個 service 正在轉換數據它可以使用一個普通的事件來派發強類型的數據給感興趣的應用程序 actor

5.  擴展點

l  Robotlegs配合as3signal使用,替代flash原生事件機制。採用as3signal可以有效減少View-Mediator的事件對象,其效率高於事件機制。

下面是flash原生機制與as3signal的性能測試對比(http://alecmce.com/as3/events-and-signals-performance-tests),測試環境:I found the following results on Mac OS X, Flash Player 10.0.42.34, with a release build.

When an event (signal) is dispatched but nothing is listening for it:

clip_image025

When an event (signal) is dispatched and handled by one method listener:

clip_image026

 

l  Robotlegs多模塊開發。Robotlegs提供一個多模塊工具:robotlegs-utilities-Modular。可以方便的進行模塊和模塊之間的通信。它看上去十分優雅,用起來也十分方便。下面是幾點要注意的地方:

n  儘量將模塊內事件和模塊間的事件分開,不要混雜着用,用於區分它們。

n  所有需要公用的對象或類(VOModelService)都在主模塊的Startup時注入,這樣子模塊都可以很方便的用到。

clip_image027http://joelhooks.com/2010/05/02/modular-robotlegs/

l  在添加大量Sprite到舞臺前,可以設置:mediatorMap.enabled=false;。完成後再設置爲true,這樣可以避免MediatorMap偵聽到ADDED_TO_STAGE事件後頻繁進行不必要的操作。也可以使用Robotlegs  LazyMediator 擴展。

爲了遊戲中有更好的性能eidiot Robotlegs 寫了一個 LazyMediator 擴展。(https://github.com/eidiot/robotlegs-utilities-LazyMediator

如何使用

n   context  override mediatorMap  getter 方法:

return _mediatorMap || (_mediatorMap = new LazyMediatorMap(contextView, injector));

n   view 類的構造函數裏增加:

new LazyMediatorActivator(this);

作用

n  LazyMediatorMap 不監聽顯示列表裏所有的 ADDED_TO_STAGE 事件而檢測所有被添加到顯示列表的顯示對象。

如何工作

n   view 被添加到 stage 或從 stage 移除時 LazyMediatorActivator 廣播 LazyMediatorEvent

n  LazyMediatorMap 監聽 context  LazyMediatorEvent 然後檢查對應的 view

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章