Robotlegs是一個用來開發Flash,Flex和AIR應用的純AS3微架構(框架)。Robotlegs專一於將應用程序各層排布在一塊兒並提供它們相互通信的機制。Robotlegs試圖經過提供一種解決常見開發問題的通過時間檢驗的架構解決方案來加速開發。Robotlegs無心鎖定你到框架,你的類就是你的類的樣子,並且應該很容易地切換到其餘框架。 git
框架提供一個基於Model-View-Controller元設計模式的默認實現。這個實現提供一個針對應用程序結構和設計的強烈建議。雖然它確實輕微減低了你的應用程序的便攜性,不過它依然以最低限度影響你的具體類爲目標。經過擴展MVCS實現類,你能夠得到不少有用的方法和屬性。 github
你沒必要使用Robotlegs的標準MVCS實現。你可使用它的任意部分,或者徹底不使用它,或者使用本身的實現來適應你的需求。它是爲了提供合適的參考實現和快速開始使用Robotlegs而被包含進來。 web
目錄 數據庫
1. MVC 編程
1.1. MVC分解 設計模式
3.1. 依賴注入 框架
訪問一個 Mediator 的 View Component
經過 Mediator 訪問 Model 和 Service
MVC就是(Model View Controller)模型-視圖-控制器,是一個設計模式(也能夠稱爲架構模式),但目前在大部分人眼裏顯然並不僅有這點意義。受到各類MVC框架的影響,消息通訊,依賴注入,隔離編譯等等概念如今都加入到了MVC的概念裏,並且由於應用上的重疊和類似性,還經常和三層架構的概念混淆。
「一千我的眼中有一千個哈姆雷特」
一個名詞的意義,是由人們的理解來決定的,所以這裏從最基本的概念入手。
對於一個用於顯示的程序,最開始,只有View(視圖)。
咱們要得到一個數據並顯示,就是寫段代碼調用接口,得到數據,而後根據數據更新視圖。而這裏的數據只是一個臨時變量,或者存在於視圖的某個屬性中。顯然,這樣作的話,數據是依賴於視圖的,若是不經過視圖,或者視圖根本不存在的時候,其餘模塊就訪問不到這個已經返回的數據,則須要從新調用一次,這顯然是一種浪費,並且也不便利。所以咱們在這裏將數據保存到別處,和視圖不共享同一輩子命週期,那麼這分離出來的數據則被規定爲Model(模型)。
(在這裏我必須打斷說明下,模型!=數據,僅僅是在這個簡化例子裏模型==數據。模型是爲視圖提供內容的單獨模塊,具體內部是什麼狀況則是各類各樣的,提供數據只是它的功能之一)
至此,就是最基本的分離表現層的原則。然而,有個地方倒是分不開的——原來控制加載數據的這部分代碼。它負責的是獲取數據,並指示視圖根據數據更新的任務,而它的去向有兩個選擇:要不加到視圖上(這是一般的作法),要不加在模型上(若是須要模型對應多個視圖),反正他不可能由於分離就憑空消失。那麼咱們把這部分代碼提出來做爲單獨的部分,就是Controller(控制器)。
一旦分離出了控制器,聯繫模型和視圖的具體代碼就再也不處於二者之上(固然調用代碼仍是有的),而是一個獨立的部分。它有本身的名字,並且不會和其餘不相關的內容混合在一塊兒,很是清晰,容易定位。而模型和視圖今後也只關心控制器,而不關心對方。他們的代碼都是處理本身的事情,別人的事情所有交給控制器去辦,這使得他們本身的功能也很是獨立,減小了須要考慮的要素。
這只是代碼位置的移動,可是這樣移動後,將牽涉內容最多最易變,可是代碼量最少的代碼單獨放入控制器中,並妥善管理。至關與將分散在房間各處的開關集中於一處,排列整齊並標註名字,作成遙控器在手裏把玩,今後就能從奔波於房間各個位置,尋找隱藏在角落裏的小突起的平常活動中解放出來。其意義,不言而喻。
而這,做爲MVC的做用,已經足夠了。
一旦咱們將視圖,模型,控制器分離後,能夠作到什麼呢?
由於視圖和模型都只用處理本身的事情,對控制器的調用只是一句代碼而已,那麼,它們實際上就能夠被隔離出去。負責這部分代碼的人只須要關心本身的事情,而不須要關心整個環境。一樣的,編寫控制器的人只須要知道視圖和模型的對外接口,而不須要了解它們的具體實現,也就是隻須要關心環境。這使得不一樣開發者須要的知識被分隔開,每人只須要了解有限的少許知識,最終卻又能順利合併在一塊兒。
這是就是協做分工的基礎。
在這以後,三者的關係只存在簡單的調用代碼。那麼爲了可以完全的分離和解耦,就能夠將調用代碼改成發送消息或者其餘的動態形式,這樣就能在沒有其餘部分的時候獨立編譯。由不一樣人在不一樣的工做環境獨立完成本身的部分,並在最後發佈時候簡單合併在一塊兒,這確實是最理想的協做模式。
作到這點須要一個消息框架,而這就是MVC框架的主要任務。除此以外,各類不一樣的框架還會加入其它設計模式,提供各類附加功能,自動依賴注入就是其中一項。還可能加入其它的中間件,進一步分割層次,諸如分離出視圖其中的邏輯部分,使得繪圖和位置代碼不會和邏輯代碼混合,方便分工以及修改。使用觀察者模式,使得數據部分的修改能夠自動同步到視圖上,如此這般……MVC框架指的是「實現MVC的框架」,而非「只實現MVC的框架」,僅僅實現MVC,這個框架的功能太過貧乏了,畢竟MVC也就是那種程度的東西。
最終,完成了這些以後,就成爲了通常人表面看到的東西。雖然各式各樣,咱們都將其稱之爲「MVC框架」。
下面對比下比較熱門的幾個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 |
「pureMVC和Cairngorm是兩個較早出現的框架,目前我不建議再使用它們。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那樣的傳遞參數方法,而是使用依賴注入,包括mediator對view組件的引用都是注入的。這樣依賴性又小了不少,感受很是不錯。」
我的認爲Robotlegs比pureMVC好用,Robotlegs是pureMVC的改進升級版並且是專一於as3的,注入技術更是省去了pureMVC的許多麻煩。
RobotLegs使用瞭如下3個面向對象設計模式:
ü 自動依賴注入(Automated Dependency Injection),不用對象本身建立、獲取依賴的對象,而是由容器/框架來完成。
ü 命令模式(Command Pattern),
n 命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開;
n 每個命令都是一個操做:請求的一方發出請求,要求執行一個操做;接收的一方收到請求,並執行操做。
n 命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求是怎麼被接收,以及操做是否被執行、什麼時候被執行,以及是怎麼被執行的。
ü 調度者模式(Mediator Pattern),定義一箇中介對象來封裝系列對象之間的交互。中介者使各個對象不須要顯示地相互引用,從而使其耦合性鬆散,並且能夠獨立地改變他們之間的交互。
Robotlegs圍繞依賴注入設計模式展開。最簡單地,依賴注入是爲對象提供實例變量或屬性的行爲。
當你傳遞一個變量到一個類的構造函數,你在使用依賴注入;
當你設置一個類的屬性,你在使用依賴注入;
若是你不是使用嚴格的過程或線性方式編寫AS3,極可能你如今就在使用依賴注入。
Robotlegs使用基於元數據的自動依賴注入。這是爲了方便開發而提供,並且在排布應用程序並提供類和它所須要的依賴時,能夠減小不少代碼量。雖然徹底能夠手動提供這些依賴,可是容許框架來履行這些職責能夠減小出錯的機會,而且一般能夠加快編碼進程。
依賴注入(Dependency Injection,簡稱DI),是一個重要的面向對象編程的法則來削減計算機程序的耦合問題。依賴注入還有一個名字叫作控制反轉(Inversion of Control,英文縮寫爲IoC)。依賴注入是這樣一個過程:因爲某客戶類只依賴於服務類的一個接口,而不依賴於具體服務類,因此客戶類只定義一個注入點。在程序運行過程當中,客戶類不直接實例化具體服務類實例,而是客戶類的運行上下文環境或專門組件負責實例化服務類,而後將其注入到客戶類中,保證客戶類的正常運行。即對象在被建立的時候,由一個運行上下文環境或專門組件將其所依賴的服務類對象的引用傳遞給它。也能夠說,依賴被注入到對象中。因此,控制反轉是,關於一個對象如何獲取他所依賴的對象的引用,這個責任的反轉。
l 依賴注入使用如下三種基本技術:
n 類型1 (基於接口): 可服務的對象須要實現一個專門的接口,該接口提供了一個對象,能夠從用這個對象查找依賴(其它服務)。
n 類型2 (基於setter): 經過屬性的setter方法爲可服務對象指定服務。
n 類型3 (基於構造函數): 經過構造函數的參數爲可服務對象指定服務。
不要重複發明輪子!
對於應用頻繁的需求,老是有人設計各類通用框架和類庫以減輕人們的開發負擔。例如,數據持久化是很是頻繁的需求,因而各類ORM框架應運而生;再如,對MVC的需求催生了Struts等一批用來實現MVC的框架。
隨着依賴注入變成了很是頻繁的需求,而若是所有手工完成,不但負擔過重,並且還容易出錯。再加上反射機制的發明,因而,天然有人開始設計開發各類用於依賴注入的專用框架。這些專門用於實現依賴注入功能的組件或框架,就是IoC Container。
IOC關注服務(或應用程序部件)是如何定義的以及他們應該如何定位他們依賴的其它服務。因此一般,經過一個容器或定位框架(著名的Spring容器、)來得到定義和定位的分離,容器或定位框架負責:
l 保存可用服務的集合
l 提供一種方式將各類部件與它們依賴的服務綁定在一塊兒
l 爲應用程序代碼提供一種方式來請求已配置的對象(例如,一個全部依賴都知足的對象), 這種方式能夠確保該對象須要的全部相關的服務均可用。
Robotlegs採用一種適配器(adapter)機制來爲框架提供依賴注入機制。默認地,框架配備了SwiftSuspenders注入/反射庫來適合這個要求。另有SmartyPants-IoC 和 Spring Actionscript 的適配器可使用。可能有潛在的特定需求來使用其它的依賴注入適配器,可是若是沒有特別的理由,建議使用默認的SwiftSuspenders,由於它爲 Robotlegs 作了一些特別調整。
SwiftSuspenders 支持三種類型的依賴注入:
ü 屬性(域)注入
ü 參數(方法/設值) 注入
ü 構造注入
咱們將特別介紹屬性注入,以及在 Robotlegs 裏如何使用。將屬性注入類有兩種選擇,你可使用未命名,或命名的注入:
[Inject]
public var myDependency:Depedency; //未命名注入
[Inject(name="myNamedDependency")]
public var myNamedDependency:NamedDepedency; //命名注入
Robotlegs裏三處提供了注入映射:MediatorMap、CommandMap、直接經過Injector。MediatorMap、CommandMap也都是使用Injector,但它們同時作了一些各自層(tier)所須要的額外工做。顧名思義,MediatorMap用來映射Mediator;CommandMap用來映射Command;其它全部須要被注入的內容 (包括但不限於 Model) 都要直接使用 Injector 映射。
具體 Injector 類的適配器都遵守 IInjector 接口。 這個接口爲不一樣的依賴注入解決方案提供了統一的API。 本文專一於 SwiftSuspenders,但這些語法一樣適應於其它任何遵守 Iinjector 接口的 Injector。
Injector是你應用程序裏所發生的全部依賴注入的生產車間。它用來注入框架 actor,同時也能夠用來執行你的應用程序所須要的任何其它注入。這包括但不限於 RemoteObjects,HTTPServices,工廠類,或者事實上任何有可能成爲你的對象所須要的依賴的類/接口。
下面是實現 IInjector 接口的類所提供的四個映射方法:
MediatorMap 實現 IMediatorMap 接口。 IMediatorMap 提供兩個方法來將你的 mediators 映射到 view 並註冊它們以便用來注入。
mapView(viewClassOrName:*,mediatorClass:Class,injectViewAs:Class = null,autoCreate:Boolean = true,autoRemove:Boolean = true):void
mapView 接受一個視圖類,MyAwesomeWidget,或者一個視圖的類全名,com.me.app.view.components::MyAwesomeWidget 做爲第一個參數。 第二個參數是將要做爲視圖組件中介的 Mediator 類。 [injectViewAs 內容未完成],最後的兩個參數 autoCreate 和 autoRemove 提供方便的自動管理 mediator 的布爾值開關。
//在你的程序裏某個映射/配置發生的地方
mediatorMap.mapView(MyAwesomeWidget,MyAwesomeWidgetMediator);
//在conntextView 的顯示列表裏的某個地方
var myAwesomeWidget:MyAwesomeWidget = new MyAwesomeWidget();
this.addChild(myAwesomeWidget); // ADDED_TO_STAGE 事件被拋出,觸發這個視圖組件的中介機制
這個方法使用了自動中介機制。
CommandMap 類實現 ICommandMap 接口,提供一個用來將 command 映射到到觸發它們的框架事件的方法。
mapEvent(eventType:String,commandClass:Class,eventClass:Class = null,oneshot:Boolean = false)
你要提供給 commandMap 一個能夠執行的類,執行它的事件類型,可選的這個事件的強類型,以及這個 command 是否只被執行一次而且隨即取消映射的布爾值開關。
這個可選的強類型事件類用來對Flash平臺的"magic string"事件類型系統作額外的保護。以免採用相同事件類型字符串的不一樣事件類之間的衝突。
//在你的程序裏某個映射/配置發生的地方
commandMap.mapEvent(MyAppDataEvent。DATA_WAS_RECEIVED,MyCoolCommand,MyAppDataEvent);
//在事件被廣播的另一個框架actor裏
//這觸發了隨後被執行的被映射的 command
dispatch(new MyAppDataEvent(MyAppDataEvent.DATA_WAS_RECEIVED,someTypedPayload))
Robotlegs提供了一個比較規範的MVC+S(Model模型,View視圖,Controller控件和Service服務)實現,來幫助開展工做。經過將幾個通過時間檢驗的設計模式整合到一個具體實現, Robotlegs 的 MVCS 實現能夠用作建立你的應用程序的一致方案。 經過這些架構概念着手一個應用程序, 你甚至能夠在開始你的設計以前避免不少常見的障礙:
ü 分離:MVCS 提供一種將你的應用程序分離到提供特定功能的無關聯的層的很天然的方法。 view 層處理用戶交互。 model 層處理用戶建立的或從外部獲取的數據。 controller 提供一種封裝各層之間複雜交互的機制。 最後, service 層提供一種和外界(好比遠程服務 API 或文件系統)交互的獨立機制。
ü 組織:經過這種分離咱們天然得到一個組織水平。 每一個項目都須要某個組織水平。 是的,有人能夠把他們全部的類都扔到頂級包下完事,但即便是最小的項目這也是不可接受的。 當一個項目有了必定的規模就須要開始組織類文件的結構了。 當向同一個應用程序開發中增長團隊成員的時候問題就更加嚴重了。 RobotLegs 的 MVCS 實現爲項目描繪出一個分爲四層的優雅的組織結構。
ü 解耦:Robotlegs 的MVCS實現將應用程序解耦爲4層。 每層都與其它層隔離, 使分離類和組件分別測試變得很是容易。除了簡化測試進程, 一般也使類更具便攜性以在其它項目中使用。 好比, 一個鏈接到遠程 API 的 Service 類可能在多個項目中都頗有用。 經過解耦這個類, 它能夠不需重構便從一個項目轉移到另外一箇中使用。
一個典型的Robotlegs MVC+S應用程序包含如下幾個部分:
ü Context:所謂Context(上下文),其實是一套自展機制,用來初始化Robotlegs所使用的依賴注入以及各類核心工具。
ü Commands:所謂Commands(命令),表明的是應用程序所能執行的獨立操做。一般,Commands(命令)會做爲對用戶操做的反應,但命令的做用並不只限於此。
ü Mediators:所謂Mediators(中介),是用來管理應用程序中的視圖組件與應用程序中的其它對象之間的信息交流。
ü Model:Models(模型)中保存着數據信息,而且表現出應用程序當前的狀態。
ü Service:Services(服務),是應用程序與外界的接口。
下面用用互操做view到獲得響應的一個流程圖:
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內有過多代碼,建議把它拆分紅ModelStartupCommand,ControllerStartupCommand和ViewStartupCommand,這樣職責更清晰。
[controller]和[view]中,若是有不少類,建議按照功能創建子目錄。
其實是一套自展機制,用來初始化Robotlegs所使用的依賴注入以及各類核心工具,一個應用程序是能夠有多個 context 的,這對想要加載外部模塊的應用程序頗有用,由於在一個 context 裏的 actor 只能在他們的 context 定義的範圍以內相互通信,因此在一個模塊化的應用程序裏,不一樣context 之間的通信是徹底可能的。
每個Robotlegs項目都以Context開始,只有Context實例化以後,Robotlegs才能啓動和運行。建立Context須要給提供一個根視圖並調用startup()方法。
每一個Rogotlegs應用程序須要一個根視圖——DisplayObjectContainter的一個實例。它將供mediatorMap使用,因此當子視view圖添加到根視圖時,對應的mediator會被建立並和view關聯起來。
啓動應用程序須要在全部配置都準備好以後,而後調用startup()方法來啓動。能夠經過將autoStartup:Boolean=true,而後該方法會自動被調用;又或者將autoStartup:Boolean=false,而後手動調用startup()方法。
在startup()方法中會初始化依賴注入規則!若是規則不多,能夠所有寫在這個該方法中;不然,建議分離到不一樣的配置類中。
Controller 層由 Command 類體現(一個Command是一個簡明、單一目的的控制器controller對象)。Command是短生命週期的無狀態對象。它們在被實例化和執行以後當即釋放。Command 應該只在處理框架事件時被執行,而不該該被任何其餘框架 actor 實例化或執行。
Command 用於應用程序各層之間相互通信,也可能用來發送系統事件。這些系統事件既可能發動其它的 Command, 也可能被一個 Mediator 接收,而後對一個 View Component 進行對應這個事件的工做。
Command 被 Context 的 CommandMap 註冊到 Context。CommandMap 在 Context 和 Command 類裏默承認用。Command 類被註冊到 Context 時接收4個參數: 一個事件類型; 響應這個事件時執行的 Command 類; 可選的事件類; 一個是否該 Command 只被執行一次隨即被取消註冊而不響應後續事件觸發的一次性設置.
Command 被 Mediators,Services,Models,和其它 Command 廣播的框架事件觸發。一個被映射的 command 在響應一個框架事件時被實例化,全部已被映射,並被 [Inject] 元數據標籤標記過的依賴都會被注入到這個 Command。另外,觸發這個 Command 的事件實例也會被注入。當這些依賴被注入完畢,Command 的執行方法會被自動調用,Command 便會進行它的工做。你不須要,並且不該該直接調用 execute() 方法。這是框架的工做。
Command 是解耦一個應用程序裏各個 actor 的很是有用的機制。由於一個 Command 永遠不會被 Mediator,Model 或者 Service 實例化或執行,這些類也就不會被耦合到 command,甚至都不知道 command 的存在.
爲了履行它們的職責,Command 可能:
ü 映射 Mediator,Model,Service,或者 Context 裏的其它 Command
ü 廣播可能被 Mediator 接收或者觸發其它 Command 的事件.
ü 被注入Model,Service,和Mediator 以直接進行工做.
須要注意的是,不建議在一個 Command 裏直接和 Mediator 交互。雖然這是可行的,但會將這個 Mediator 耦合到這個 Command。由於 Mediator 不像Service 和 Model,它能夠接受系統事件,更好的作法是讓 Command 廣播事件,而後讓須要響應這些事件的 Mediator 監聽它們。
View 由 Mediator 類體現。繼承 Mediator 的類管理應用程序中的 View Component 與應用程序中的其它對象之間的信息交流。一個Mediator 將會監聽框架事件和 View Component 事件,並在處理所負責的 View Component 發出的事件時發送框架事件。這樣開發者能夠將應用程序特有的邏輯放到Mediator,而避免把 View Component 耦合到特定的應用程序。
注意,Mediator僅僅是框架和View Component之間的橋樑,這也是他惟一的職責。
一個 View Component 是任何的UI組件和/或它的子組件。一個 View Component 是已被封裝的,儘量多地處理本身的狀態和操做。一個 View Component 提供一個包含了事件,簡單方法和屬性的API。Mediators負責表明它所中介的View Component和框架交互。這包括監聽組件及其子組件的事件,調用其方法,和讀取/設置組件的屬性.
一個 Mediator 監聽它的 View Component 的事件,經過 View Component 暴露的 API 訪問其數據。一個 Mediators 經過響應其它框架 actor 的事件並對本身的 View Component 進行相應修改來表明它們。一個 Mediator 經過轉發 View Component 的事件或本身向框架廣播合適的事件來通知其它的框架 actor.
任何能夠訪問到mediatorMap實例的類均可以映射 Mediator。這包括 Mediator,Context,和 Command 類.
這是映射一個 mediator 的語法:
mediatorMap.mapView( ViewClass,MediatorClass,autoCreate,autoRemove );
當view添加到舞臺上時,Mediator會被自動建立並關聯起來。
當view從舞臺上移除時,Mediator會被自動清除。
當一個 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( eventDispatcher,MyCustomEvent.DO_STUFF,handleDoStuff );
//添加一個事件監聽器到被中介的 view component
eventMap.mapListener( myCustomComponent,MyCustomEvent.DID_SOME_STUFF,handleDidSomeStuff)
}
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 的眼睛和鼻子。由於框架內的全部通信都經過原生的Flash事件,Mediator 能夠經過添加事件監聽器來響應感興趣的事件。除了框架事件,Mediator同時監聽所中介的 view component 的事件.
一般在 Mediator 的 onRegister 方法裏添加事件監聽。在 Mediator 生命週期中的這個階段,它已經被註冊而且它的 view component 和其它依賴也都已被注入。具體的 Mediator 類必須覆寫 onRegister 方法。也能夠在其它方法裏添加事件監聽,好比響應框架事件和 view component 事件的事件處理方法裏.
Mediators 裝備了一個有 mapListener() 方法的 EventMap。這個方法註冊每一個被添加到 Mediator 的事件監聽,而且確保 mediator 被框架取消註冊時刪除這些事件監聽。Flash 裏刪除事件監聽是很重要的,由於若是一個類裏添加了事件監聽而沒有刪除,Player將沒法對此類進行運行時垃圾回收(GC,Garbage Collection)。也可使用傳統的 Flash 語法添加事件監聽器,但要注意也要手動把它們刪除。
全部框架裏的actor在實例化時都會被注入一個eventDispatcher屬性。這個eventDispatcher就是 Mediator 發送和接受框架事件的機制.
eventMap.mapListener(eventDispatcher,SomeEvent.IT_IS_IMPORTANT,handleFrameworkEvent)
經過此語法,一個 Mediator 如今監聽了SomeEvent.IT_IS_IMPORTANT事件並在handleFrameworkEvent方法裏處理它。
Mediator的一個很重要的職責就是向框架發送其它 actor 可能感興趣的事件。這些事件一般是在響應應用程序用戶和被中介的 view component 之間的一些交互時發出的。這裏一樣有一個能夠減小發送事件到框架的代碼輸入的頗有用的方法:
dispatch(new SomeEvent(SomeEvent.YOU_WILL_WANT_THIS,myViewComponent.someData))
這個事件如今能夠被其它 Mediator 接收或者執行一個 command 了。發出事件的 Mediator 並不關心其它的 actor 如何迴應這個事件,它只是簡單地廣播一條有事發生的消息。一個 mediator 也能夠監聽本身發出的事件,而後據此做出迴應.
Mediator 負責所中介的 view component 發出的事件。這能夠是個獨立組件,好比 TextField 或者 Button,也能夠是有嵌套層級的複雜組件。當 mediator收到 view component 發出的事件會使用指定的方法處理它。和框架事件同樣,EventMap 的 mapListener 方法是給一個 mediator 添加事件監聽的首選.
eventMap.mapListener(myMediatedViewComponent,SomeEvent.USER_DID_SOMETHING,handleUserDidSomethingEvent)
響應一個 view component 的事件時,一個 mediator 可能:
ü 考察 view component 的當前狀態
ü 對view component 進行須要的工做
ü 發送系統事件以通知其它actor有事發生
你的 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。這種作法是 強烈不建議的 由於這種緊耦合能夠簡單地經過使用框架事件進行通信而避免。
Model 類用來管理對應用程序的數據模型的訪問。Model 爲其它框架actor提供一個 API 來訪問,操做和更新應用程序數據。這個數據包括但不限於原生數據類型好比 String,Array,或者像 ArrayCollection 同樣的域特有對象或集合.
Model 有時被當作簡單的 Model 好比 UserModel,有時也被當作 Proxy 好比 UserProxy。在 Robotlegs 裏,這兩種命名都是用做相同的目的,爲應用程序數據提供一個 API。無論採用哪一種命名 model 都繼承提供了核心框架依賴和一些有用方法的 Actor 基類。
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 裏,因此能夠很容易被兩個徹底不一樣元素的視圖複用.
Injector 有幾個方法能夠用來將你的 Model 類映射到你的框架actor。另外,這些方法事實上能夠用來注入任何類到你的類裏.
將一個已存在的實例當作一個單例注入映射,使用下面的語法:
injector.mapValue(MyModelClass,myModelClassInstance)
爲每一個注入映射一個類的新實例,使用下面的語法:
injector.mapClass(MyModelClass,MyModelClass)
另外,這也能夠用來使用被注入的實現某接口的合適的類來映射這個用來注入的接口.
injector.mapClass(IMyModelClass,MyModelClass)
爲某個接口或類映射一個單例實例,使用下面的語法:
injector.mapSingleton(MyModelClass,MyModelClass)
須要注意重要的一點,當說起上面的一個單例時,它並非一個單例模式的單例。在這個 Context 以外並不強制它做爲一個單例。Injector 簡單地確保這個類的惟一一個實例被注入。這對處理你的應用程序數據模型的 Model 很是重要.
Model 類提供一個方便的dispatch方法用來發送框架事件:
dispatch( new ImportantDataEvent(ImportantDataEvent.IMPORTANT_DATA_UPDATED))
有不少理由派發一個事件,包括但不限於:
ü 數據已被初始化並準備好被其它 actor 使用
ü 一些數據片被添加到 Model
ü 數據被從 Model 中刪除
ü 數據已改變或者更新
ü 數據相關的狀態已改變
在一個 Model 裏監聽框架事件
雖然技術上可能,但強烈不建議這樣作。不要這樣作。只是爲了說清楚:不要這樣作。若是你這樣作了,不要說你沒被警告過.
Service 用來訪問應用程序範圍以外的資源。這包括但固然不限於:
ü web services
ü 文件系統
ü 數據庫
ü RESTful APIs
ü 經過 localConnection 的其它 Flash 應用程序
Service 封裝了這些和外部實體的交互,並管理這個交互產生的 result ,fault 或其它事件.
你可能注意到 Service 和 Model 的基類很是相像。事實上,你可能注意到除了類名,它們實際上是同樣的。那麼爲何用兩個類呢? Model 和 Service 類在一個應用程序裏有徹底不一樣的職責。這些類的具體實現將再也不相像。若是沒有這個分離,你將常常發現 Model 類在訪問外部服務。這讓 Model 有不少職責,訪問外部數據,解析結果,處理失敗,管理應用程序數據狀態,爲數據提供一個 API,爲外部服務提供一個 API,等等。經過分離這些層有助於緩解這個問題。
一個 Service 類爲你的應用程序提供一個和外部服務交互的 API。一個 service 類將鏈接外部服務並管理它收到的響應。Service 類一般是無狀態的實體。他們並不存儲從外部服務收到的數據,而是發送框架事件來讓合適的框架 actor 管理響應數據和失敗.
有injector 的多個可用的方法能夠用來映射你的 Service 類以注入你的其它框架 actor。另外,這些方法也能夠用來注入事實上任何類到你的類裏。
將一個已存在的實例當作一個單例注入映射,使用下面的語法:
injector.mapValue(MyServiceClass,myServiceClassInstance)
爲每一個注入映射一個類的新實例,使用下面的語法:
injector.mapClass(MyServiceClass,MyServiceClass)
另外,這也能夠用來使用被注入的實現某接口的合適的類來映射這個用來注入的接口.
injector.mapClass(IMyServiceClass,MyServiceClass)
爲某個接口或類映射一個單例實例,使用下面的語法:
injector.mapSingleton(MyServiceClass,MyServiceClass)
須要注意重要的一點,當說起上面的一個單例時,它並非一個單例模式的單例。在這個 Context 以外並不強制它做爲一個單例。Injector 簡單地確保這個類的惟一一個實例被注入.
在一個 Service 裏監聽框架事件
雖然技術上可能,但 強烈不建議 這樣作。不要這樣作。只是爲了說清楚: 不要這樣作。若是你這樣作了,不要說你沒被警告過.
Service 類提供一個方便的dispatch方法用來發送框架事件:
dispatch( new ImportantServiceEvent(ImportantServiceEvent.IMPORTANT_SERVICE_EVENT))
從外部獲取到的數據並不符合咱們應用程序的 context。它們是外來者。能夠圍繞外部數據類型對應用程序進行建模,或者更可取地,轉換這些數據以符合應用程序。應用程序裏有兩處能夠進行這項操做/轉換。Service 和 Model 都很適合。Service 是進入外部數據的第一個點,因此它是操做一個外部服務返回的數據的更好的選擇。外來數據應該在第一個機會轉換到應用程序域。
提供一個使用工廠類而不是在 service 裏生成應用程序域對象的例子… 適當的
當數據被轉換爲應用程序域特有的對象以後發出帶有強類型負載的事件以被對此關心的 actor 當即使用.
service 組合的最後一個部分是自定義事件。沒有事件的 service 只是啞吧。他們可能作的任何工做都不會被其它框架成員注意到。一個 service 將會使用自定義事件來嚮應用程序發出聲音。事件並不必定是惟一的意圖。若是這個 service 正在轉換數據它可使用一個普通的事件來派發強類型的數據給感興趣的應用程序 actor。
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:
When an event (signal) is dispatched and handled by one method listener:
l Robotlegs多模塊開發。Robotlegs提供一個多模塊工具:robotlegs-utilities-Modular。能夠方便的進行模塊和模塊之間的通訊。它看上去十分優雅,用起來也十分方便。下面是幾點要注意的地方:
n 儘可能將模塊內事件和模塊間的事件分開,不要混雜着用,用於區分它們。
n 全部須要公用的對象或類(VO,Model,Service)都在主模塊的Startup時注入,這樣子模塊均可以很方便的用到。
(http://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。