ROBOTLEGS 輕量級AS3框架

任何一個好的東西(語言、框架等)最終還取決于用的人

語言和框架本身并不能保證用戶的代碼清晰、解耦等,

當然它只是盡可能地做到這點。

Robotlegs是一個用來開發(fā)Flash,F(xiàn)lex和AIR應(yīng)用的純AS3微架構(gòu)(框架)。Robotlegs專注于將應(yīng)用程序各層排布在一起并提供它們相互通訊的機制。Robotlegs試圖通過提供一種解決常見開發(fā)問題的經(jīng)過時間檢驗的架構(gòu)解決方案來加速開發(fā)。Robotlegs無意鎖定你到框架,你的類就是你的類的樣子,而且應(yīng)該很容易地切換到其他框架。

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

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

目錄

1.?MVC

1.1.?MVC分解

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

2.?ROBOTLEGS與其它框架的比較

3.?ROBOTLEGS的依賴注入

3.1.?依賴注入

3.2.?SwiftSuspenders

SwiftSuspenders?適配器注入語法

4.?ROBOTLEGS的標準MVCS實現(xiàn)

4.1.?Robotlegs MVC+S

4.2.?推薦目錄結(jié)構(gòu)

4.3.?Context

提供根視圖(root-view)

調(diào)用startup()方法

4.4.?Command

Command?職責

觸發(fā)?Command

應(yīng)用程序?qū)拥慕怦?/i>

4.5.?View & Mediator

Mediator?職責

映射一個?Mediator

訪問一個?Mediator?的?View Component

給一個?Mediator?添加事件監(jiān)聽

監(jiān)聽框架事件

廣播框架事件

監(jiān)聽?View Component?事件

通過?Mediator?訪問?Model?和?Service

4.6.?Models

Model?職責

映射一個?Model

從一個Model里廣播事件

4.7.?Services

Service?職責

映射一個?Service

廣播框架事件

在一個?Service?里解析數(shù)據(jù)

Service?事件

5.?擴展點

參考文獻



1.?MVC

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

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

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

1.1.?MVC分解

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

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

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

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

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

這只是代碼位置的移動,但是這樣移動后,將牽涉內(nèi)容最多最易變,但是代碼量最少的代碼單獨放入控制器中,并妥善管理。相當與將分散在房間各處的開關(guān)集中于一處,排列整齊并標注名字,做成遙控器在手里把玩,從此就能從奔波于房間各個位置,尋找隱藏在角落里的小突起的日?;顒又薪夥懦鰜?。其意義,不言而喻。

而這,作為MVC的作用,已經(jīng)足夠了。

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

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

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

這是就是協(xié)作分工的基礎(chǔ)。

在這之后,三者的關(guān)系只存在簡單的調(diào)用代碼。那么為了能夠徹底的分離和解耦,就可以將調(diào)用代碼改為發(fā)送消息或者其他的動態(tài)形式,這樣就能在沒有其他部分的時候獨立編譯。由不同人在不同的工作環(huán)境獨立完成自己的部分,并在最后發(fā)布時候簡單合并在一起,這確實是最理想的協(xié)作模式。

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

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

2.?RobotLegs與其它框架的比較

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

FrameworkDependencies ManagementEvent ManagementPresentation Pattern

CairngormSingletonSingleton DispatcherCode Behind

PureMVCService LocatorNotificationMediator

MateDependency InjectionDisplay listPresentation Model

SwizDependency InjectionDisplay listPresentation Model

ParsleyDependency InjectionCentral DispatcherPresentation Model

RobotlegsDependency InjectionEvent busMediator or others

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

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

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

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

個人認為Robotlegs比pureMVC好用,Robotlegs是pureMVC的改進升級版而且是專注于as3的,注入技術(shù)更是省去了pureMVC的許多麻煩。

3.?RobotLegs的依賴注入

RobotLegs使用了以下3個面向?qū)ο笤O(shè)計模式:

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

ü?命令模式(Command Pattern

n?命令模式的本質(zhì)是對命令進行封裝,將發(fā)出命令的責任和執(zhí)行命令的責任分割開;

n?每一個命令都是一個操作:請求的一方發(fā)出請求,要求執(zhí)行一個操作;接收的一方收到請求,并執(zhí)行操作。

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

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

Robotlegs圍繞依賴注入設(shè)計模式展開。最簡單地,依賴注入是為對象提供實例變量或?qū)傩缘男袨椤?/p>

當你傳遞一個變量到一個類的構(gòu)造函數(shù),你在使用依賴注入;

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

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

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

3.1.?依賴注入

依賴注入(Dependency Injection,簡稱DI),是一個重要的面向?qū)ο缶幊痰姆▌t來削減計算機程序的耦合問題。依賴注入還有一個名字叫做控制反轉(zhuǎn)(Inversion of Control,英文縮寫為IoC)。依賴注入是這樣一個過程:由于某客戶類只依賴于服務(wù)類的一個接口,而不依賴于具體服務(wù)類,所以客戶類只定義一個注入點。在程序運行過程中,客戶類不直接實例化具體服務(wù)類實例,而是客戶類的運行上下文環(huán)境專門組件負責實例化服務(wù)類,然后將其注入到客戶類中,保證客戶類的正常運行。即對象在被創(chuàng)建的時候,由一個運行上下文環(huán)境或?qū)iT組件將其所依賴的服務(wù)類對象的引用傳遞給它。也可以說,依賴被注入到對象中。所以,控制反轉(zhuǎn)是,關(guān)于一個對象如何獲取他所依賴的對象的引用,這個責任的反轉(zhuǎn)。

l?依賴注入使用以下三種基本技術(shù):

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

n?類型2 (基于setter): 通過屬性的setter方法為可服務(wù)對象指定服務(wù)。

n?類型3 (基于構(gòu)造函數(shù)): 通過構(gòu)造函數(shù)的參數(shù)為可服務(wù)對象指定服務(wù)。

不要重復(fù)發(fā)明輪子!

對于應(yīng)用頻繁的需求,總是有人設(shè)計各種通用框架和類庫以減輕人們的開發(fā)負擔。例如,數(shù)據(jù)持久化是非常頻繁的需求,于是各種ORM框架應(yīng)運而生;再如,對MVC的需求催生了Struts等一批用來實現(xiàn)MVC的框架。

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

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

l?保存可用服務(wù)的集合

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

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


3.2.?SwiftSuspenders

Robotlegs采用一種適配器(adapter)機制來為框架提供依賴注入機制。默認地,框架配備了SwiftSuspenders注入/反射庫來適合這個要求。另有SmartyPants-IoC?和?Spring Actionscript?的適配器可以使用??赡苡袧撛诘奶囟ㄐ枨髞硎褂闷渌囊蕾囎⑷脒m配器,但是如果沒有特別的理由,建議使用默認的SwiftSuspenders,因為它為?Robotlegs?做了一些特別調(diào)整。

SwiftSuspenders?適配器注入語法

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

ü?屬性(域)注入

ü?參數(shù)(方法/設(shè)值)?注入

ü?構(gòu)造注入

我們將特別介紹屬性注入,以及在?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;其它所有需要被注入的內(nèi)容?(包括但不限于Model)?都要直接使用?Injector?映射。

Injector?類的映射注入

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

Injector是你應(yīng)用程序里所發(fā)生的所有依賴注入的生產(chǎn)車間。它用來注入框架?actor,同時也可以用來執(zhí)行你的應(yīng)用程序所需要的任何其它注入。這包括但不限于?RemoteObjects,HTTPServices,工廠類,或者事實上任何有可能成為你的對象所需要的依賴的類/接口。

下面是實現(xiàn)?IInjector?接口的類所提供的四個映射方法:

MediatorMap?類的依賴注入

MediatorMap?實現(xiàn)?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?作為第一個參數(shù)。?第二個參數(shù)是將要作為視圖組件中介的?Mediator?類。?[injectViewAs?內(nèi)容未完成],最后的兩個參數(shù)?autoCreate?和?autoRemove?提供方便的自動管理?mediator?的布爾值開關(guān)。

//在你的程序里某個映射/配置發(fā)生的地方

mediatorMap.mapView(MyAwesomeWidget,MyAwesomeWidgetMediator);

//在conntextView?的顯示列表里的某個地方

var myAwesomeWidget:MyAwesomeWidget = new MyAwesomeWidget();

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

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

CommandMap?類的依賴注入

CommandMap?類實現(xiàn)?ICommandMap?接口,提供一個用來將?command?映射到到觸發(fā)它們的框架事件的方法。

mapEvent(eventType:String,commandClass:Class,eventClass:Class = null,oneshot:Boolean = false)

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

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

//在你的程序里某個映射/配置發(fā)生的地方

commandMap.mapEvent(MyAppDataEvent。DATA_WAS_RECEIVED,MyCoolCommand,MyAppDataEvent);

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

//這觸發(fā)了隨后被執(zhí)行的被映射的?command

dispatch(new MyAppDataEvent(MyAppDataEvent.DATA_WAS_RECEIVED,someTypedPayload))

4.?Robotlegs的標準MVCS實現(xiàn)

Robotlegs提供了一個比較規(guī)范的MVC+S(Model模型,View視圖,Controller控件和Service服務(wù))實現(xiàn),來幫助開展工作。通過將幾個經(jīng)過時間檢驗的設(shè)計模式整合到一個具體實現(xiàn),?Robotlegs?的?MVCS?實現(xiàn)可以用做創(chuàng)建你的應(yīng)用程序的一致方案。?通過這些架構(gòu)概念著手一個應(yīng)用程序,?你甚至可以在開始你的設(shè)計之前避免很多常見的障礙:

ü?分離:MVCS?提供一種將你的應(yīng)用程序分離到提供特定功能的無關(guān)聯(lián)的層的很自然的方法。?view?層處理用戶交互。?model?層處理用戶創(chuàng)建的或從外部獲取的數(shù)據(jù)。?controller?提供一種封裝各層之間復(fù)雜交互的機制。 最后,?service?層提供一種和外界(比如遠程服務(wù)?API?或文件系統(tǒng))交互的獨立機制。

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

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

4.1.?Robotlegs MVC+S

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

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

ü?Commands:所謂Commands(命令),代表的是應(yīng)用程序所能執(zhí)行的獨立操作。通常,Commands(命令)會作為對用戶操作的反應(yīng),但命令的作用并不僅限于此。

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

ü?ModelModels(模型)中保存著數(shù)據(jù)信息,并且表現(xiàn)出應(yīng)用程序當前的狀態(tài)。

ü?Service:Services(服務(wù)),是應(yīng)用程序與外界的接口。

下面用用互操作view到得到響應(yīng)的一個流程圖:


4.2.?推薦目錄結(jié)構(gòu)

Robotlegs代碼包結(jié)構(gòu)組織:

[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建議的包結(jié)構(gòu)。這是一個多模塊情況的包結(jié)構(gòu),如果是單模塊,可以少去projectmodule這一層。[signals]包不是必須的,除非你用到了Signals。

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

StartupCommand內(nèi)代碼建議以MVC結(jié)構(gòu)分離。如果一個模塊中StartupCommnad內(nèi)有過多代碼,建議把它拆分成ModelStartupCommand,ControllerStartupCommand和ViewStartupCommand,這樣職責更清晰。

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

4.3.?Context

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

每一個Robotlegs項目都以Context開始,只有Context實例化之后,Robotlegs才能啟動和運行。創(chuàng)建Context需要給提供一個根視圖并調(diào)用startup()方法。

提供根視圖(root-view

每個Rogotlegs應(yīng)用程序需要一個根視圖——DisplayObjectContainter的一個實例。它將供mediatorMap使用,所以當子視view圖添加到根視圖時,對應(yīng)的mediator會被創(chuàng)建并和view關(guān)聯(lián)起來。

調(diào)用startup()方法

啟動應(yīng)用程序需要在所有配置都準備好之后,然后調(diào)用startup()方法來啟動??梢酝ㄟ^將autoStartup:Boolean=true,然后該方法會自動被調(diào)用;又或者將autoStartup:Boolean=false,然后手動調(diào)用startup()方法。

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

4.4.?Command

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

Command?職責

Command?用于應(yīng)用程序各層之間相互通訊,也可能用來發(fā)送系統(tǒng)事件。這些系統(tǒng)事件既可能發(fā)動其它的?Command,?也可能被一個?Mediator?接收,然后對一個?View Component?進行對應(yīng)這個事件的工作。

Command?被?Context?的?CommandMap?注冊到?Context。CommandMap?在?Context?和?Command?類里默認可用。Command?類被注冊到?Context?時接收4個參數(shù):一個事件類型;?響應(yīng)這個事件時執(zhí)行的?Command?類;?可選的事件類;?一個是否該?Command?只被執(zhí)行一次隨即被取消注冊而不響應(yīng)后續(xù)事件觸發(fā)的一次性設(shè)置.

觸發(fā)?Command

Command?被?Mediators,Services,Models,和其它?Command?廣播的框架事件觸發(fā)。一個被映射的?command?在響應(yīng)一個框架事件時被實例化,所有已被映射,并被?[Inject]?元數(shù)據(jù)標簽標記過的依賴都會被注入到這個?Command。另外,觸發(fā)這個?Command?的事件實例也會被注入。當這些依賴被注入完畢,Command?的執(zhí)行方法會被自動調(diào)用,Command?便會進行它的工作。你不需要,而且不應(yīng)該直接調(diào)用?execute()?方法。這是框架的工作。

應(yīng)用程序?qū)拥慕怦?/a>

Command?是解耦一個應(yīng)用程序里各個?actor?的非常有用的機制。因為一個?Command?永遠不會被?Mediator,Model?或者?Service?實例化或執(zhí)行,這些類也就不會被耦合到?command,甚至都不知道?command?的存在.

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

ü?映射?Mediator,Model,Service,或者?Context?里的其它?Command

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

ü?被注入Model,Service,和Mediator?以直接進行工作.

需要注意的是,不建議在一個?Command?里直接和?Mediator?交互。雖然這是可行的,但會將這個?Mediator?耦合到這個?Command。因為?Mediator?不像Service?和?Model,它可以接受系統(tǒng)事件,更好的做法是讓?Command?廣播事件,然后讓需要響應(yīng)這些事件的?Mediator?監(jiān)聽它們。


4.5.?View & Mediator

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

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

Mediator?職責

一個?View Component?是任何的UI組件和/或它的子組件。一個?View Component?是已被封裝的,盡可能多地處理自己的狀態(tài)和操作。一個?View Component提供一個包含了事件,簡單方法和屬性的API。Mediators負責代表它所中介的View Component和框架交互。這包括監(jiān)聽組件及其子組件的事件,調(diào)用其方法,和讀取/設(shè)置組件的屬性.

一個?Mediator?監(jiān)聽它的?View Component?的事件,通過?View Component?暴露的?API?訪問其數(shù)據(jù)。一個?Mediators?通過響應(yīng)其它框架?actor?的事件并對自己的View Component?進行相應(yīng)修改來代表它們。一個?Mediator?通過轉(zhuǎn)發(fā)?View Component?的事件或自己向框架廣播合適的事件來通知其它的框架?actor.

映射一個?Mediator

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

這是映射一個?mediator?的語法:

mediatorMap.mapView( ViewClass,MediatorClass,autoCreate,autoRemove );

當view添加到舞臺上時,Mediator會被自動創(chuàng)建并關(guān)聯(lián)起來。

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

訪問一個?Mediator?的?View Component

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

public class GalleryLabelMediator extends Mediator implements IMediator

{

?[Inject]

?public var myCustomComponent:MyCustomComponent;

????????????

?/**

?*?覆寫?onRegister?是添加此?Mediator?關(guān)心的任何系統(tǒng)或?View Component?事件的好機會.

?*/

?override public function onRegister():void

?{

?//添加一個事件監(jiān)聽器到?Context?來監(jiān)聽框架事件

?eventMap.mapListener( eventDispatcher,MyCustomEvent.DO_STUFF,handleDoStuff );

?//添加一個事件監(jiān)聽器到被中介的?view component

?eventMap.mapListener( myCustomComponent,MyCustomEvent.DID_SOME_STUFF,handleDidSomeStuff)

?}

?????

?protected function handleDoStuff(event:MyCustomEvent):void

?{

?//把事件的強類型負載設(shè)置到?view component?的屬性。

?//View component?很可能基于這個新數(shù)據(jù)管理自己的狀態(tài).

?myCustomComponent.aProperty = event.payload

?}

?????

?protected function handleDidSomeStuff(event:MyCustomEvent):void

?{

?//把這個事件轉(zhuǎn)發(fā)到框架

?dispatch(event)

?}

}

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

給一個?Mediator?添加事件監(jiān)聽

事件監(jiān)聽器是?Mediator?的眼睛和鼻子。因為框架內(nèi)的所有通訊都通過原生的Flash事件,Mediator?可以通過添加事件監(jiān)聽器來響應(yīng)感興趣的事件。除了框架事件,Mediator同時監(jiān)聽所中介的?view component?的事件.

通常在?Mediator?的?onRegister?方法里添加事件監(jiān)聽。在?Mediator?生命周期中的這個階段,它已經(jīng)被注冊并且它的?view component?和其它依賴也都已被注入。具體的?Mediator?類必須覆寫?onRegister?方法。也可以在其它方法里添加事件監(jiān)聽,比如響應(yīng)框架事件和?view component?事件的事件處理方法里.

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

監(jiān)聽框架事件

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

eventMap.mapListener(eventDispatcher,SomeEvent.IT_IS_IMPORTANT,handleFrameworkEvent)

通過此語法,一個?Mediator?現(xiàn)在監(jiān)聽了SomeEvent.IT_IS_IMPORTANT事件并在handleFrameworkEvent方法里處理它。

廣播框架事件

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

dispatch(new SomeEvent(SomeEvent.YOU_WILL_WANT_THIS,myViewComponent.someData))

這個事件現(xiàn)在可以被其它?Mediator?接收或者執(zhí)行一個?command?了。發(fā)出事件的?Mediator?并不關(guān)心其它的?actor?如何回應(yīng)這個事件,它只是簡單地廣播一條有事發(fā)生的消息。一個?mediator?也可以監(jiān)聽自己發(fā)出的事件,然后據(jù)此作出回應(yīng).

監(jiān)聽?View Component?事件

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

eventMap.mapListener(myMediatedViewComponent,SomeEvent.USER_DID_SOMETHING,handleUserDidSomethingEvent)

響應(yīng)一個?view component?的事件時,一個?mediator?可能:

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

ü?考察?view component?的當前狀態(tài)

ü?對view component?進行需要的工作

ü?發(fā)送系統(tǒng)事件以通知其它actor有事發(fā)生

通過?Mediator?訪問?Model?和?Service

你的?mediator?可以監(jiān)聽?Service?和?Model?類派出的系統(tǒng)事件來提高松耦合性。通過監(jiān)聽事件,你的?mediator?不需要關(guān)心事件來源,而只需直接使用事件攜帶的強類型的負載payload數(shù)據(jù)。因此,多個?mediator?可以監(jiān)聽相同的事件然后根據(jù)所收到的數(shù)據(jù)調(diào)整自己的狀態(tài).

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

建議通過?model?和?service?實現(xiàn)的接口將?model?和?service?注入?mediator。

訪問其它?Mediator

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


4.6.?Models

Model?類用來管理對應(yīng)用程序的數(shù)據(jù)模型的訪問。Model?為其它框架actor提供一個?API?來訪問,操作和更新應(yīng)用程序數(shù)據(jù)。這個數(shù)據(jù)包括但不限于原生數(shù)據(jù)類型比如?String,Array,或者像?ArrayCollection?一樣的域特有對象或集合.

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

Model?會在對數(shù)據(jù)模型進行某些工作之后發(fā)出事件通知。Model?通常具有極高的便攜性。

Model?職責

Model類封裝了應(yīng)用程序數(shù)據(jù)模型并為其提供一個?API。一個?Model?類是你的應(yīng)用程序數(shù)據(jù)的看門人。應(yīng)用程序里的其它?actor?通過?Model?提供的?API請求數(shù)據(jù)。因為數(shù)據(jù)是通過?Model?更新,Model?裝備了向框架廣播事件的機制以向其它?actor?通知數(shù)據(jù)模型的變化使它們得以據(jù)此調(diào)整自己的狀態(tài).

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

映射一個?Model

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

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

injector.mapValue(MyModelClass,myModelClassInstance)

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

injector.mapClass(MyModelClass,MyModelClass)

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

injector.mapClass(IMyModelClass,MyModelClass)

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

injector.mapSingleton(MyModelClass,MyModelClass)

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

從一個Model里廣播事件

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

dispatch( new ImportantDataEvent(ImportantDataEvent.IMPORTANT_DATA_UPDATED))

有很多理由派發(fā)一個事件,包括但不限于:

ü?數(shù)據(jù)已被初始化并準備好被其它?actor?使用

ü?一些數(shù)據(jù)片被添加到?Model

ü?數(shù)據(jù)被從?Model?中刪除

ü?數(shù)據(jù)已改變或者更新

ü?數(shù)據(jù)相關(guān)的狀態(tài)已改變

在一個?Model?里監(jiān)聽框架事件

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

4.7.?Services

Service?用來訪問應(yīng)用程序范圍之外的資源。這包括但當然不限于:

ü?web services

ü?文件系統(tǒng)

ü?數(shù)據(jù)庫

ü?RESTful APIs

ü?通過?localConnection?的其它?Flash?應(yīng)用程序

Service?封裝了這些和外部實體的交互,并管理這個交互產(chǎn)生的?result?,fault?或其它事件.

你可能注意到?Service?和?Model?的基類非常相像。事實上,你可能注意到除了類名,它們其實是一樣的。那么為什么用兩個類呢? Model?和?Service?類在一個應(yīng)用程序里有完全不同的職責。這些類的具體實現(xiàn)將不再相像。如果沒有這個分離,你將經(jīng)常發(fā)現(xiàn)?Model?類在訪問外部服務(wù)。這讓?Model?有很多職責,訪問外部數(shù)據(jù),解析結(jié)果,處理失敗,管理應(yīng)用程序數(shù)據(jù)狀態(tài),為數(shù)據(jù)提供一個?API,為外部服務(wù)提供一個?API,等等。通過分離這些層有助于緩解這個問題。

Service?職責

一個?Service?類為你的應(yīng)用程序提供一個和外部服務(wù)交互的?API。一個?service?類將連接外部服務(wù)并管理它收到的響應(yīng)。Service?類通常是無狀態(tài)的實體。他們并不存儲從外部服務(wù)收到的數(shù)據(jù),而是發(fā)送框架事件來讓合適的框架?actor?管理響應(yīng)數(shù)據(jù)和失敗.

映射一個?Service

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

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

injector.mapValue(MyServiceClass,myServiceClassInstance)

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

injector.mapClass(MyServiceClass,MyServiceClass)

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

injector.mapClass(IMyServiceClass,MyServiceClass)

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

injector.mapSingleton(MyServiceClass,MyServiceClass)

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

在一個?Service?里監(jiān)聽框架事件

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

廣播框架事件

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

dispatch( new ImportantServiceEvent(ImportantServiceEvent.IMPORTANT_SERVICE_EVENT))

在一個?Service?里解析數(shù)據(jù)

從外部獲取到的數(shù)據(jù)并不符合我們應(yīng)用程序的?context。它們是外來者??梢試@外部數(shù)據(jù)類型對應(yīng)用程序進行建模,或者更可取地,轉(zhuǎn)換這些數(shù)據(jù)以符合應(yīng)用程序。應(yīng)用程序里有兩處可以進行這項操作/轉(zhuǎn)換。Service?和?Model?都很適合。Service?是進入外部數(shù)據(jù)的第一個點,所以它是操作一個外部服務(wù)返回的數(shù)據(jù)的更好的選擇。外來數(shù)據(jù)應(yīng)該在第一個機會轉(zhuǎn)換到應(yīng)用程序域。

提供一個使用工廠類而不是在?service?里生成應(yīng)用程序域?qū)ο蟮睦印?適當?shù)?/i>

當數(shù)據(jù)被轉(zhuǎn)換為應(yīng)用程序域特有的對象之后發(fā)出帶有強類型負載的事件以被對此關(guān)心的?actor?立即使用.

Service?事件

service?組合的最后一個部分是自定義事件。沒有事件的?service?只是啞巴。他們可能做的任何工作都不會被其它框架成員注意到。一個?service?將會使用自定義事件來向應(yīng)用程序發(fā)出聲音。事件并不一定是唯一的意圖。如果這個?service?正在轉(zhuǎn)換數(shù)據(jù)它可以使用一個普通的事件來派發(fā)強類型的數(shù)據(jù)給感興趣的應(yīng)用程序?actor。

5.?擴展點

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

下面是flash原生機制與as3signal的性能測試對比(http://alecmce.com/as3/events-and-signals-performance-tests),測試環(huán)境: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多模塊開發(fā)。Robotlegs提供一個多模塊工具:robotlegs-utilities-Modular??梢苑奖愕倪M行模塊和模塊之間的通信。它看上去十分優(yōu)雅,用起來也十分方便。下面是幾點要注意的地方:

n?盡量將模塊內(nèi)事件和模塊間的事件分開,不要混雜著用,用于區(qū)分它們。

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

(http://joelhooks.com/2010/05/02/modular-robotlegs/)

l?在添加大量Sprite到舞臺前,可以設(shè)置:mediatorMap.enabled=false;。完成后再設(shè)置為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?類的構(gòu)造函數(shù)里增加:

new?LazyMediatorActivator(this);

作用

n?LazyMediatorMap?不監(jiān)聽顯示列表里所有的?ADDED_TO_STAGE?事件而檢測所有被添加到顯示列表的顯示對象。

如何工作

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

n?LazyMediatorMap?監(jiān)聽?context?的?LazyMediatorEvent?然后檢查對應(yīng)的?view。


參考文獻

[1]?robotlegs-best-practice(zh):https://github.com/robotlegs/robotlegs-documentation/blob/master/best-practices-zh-cn.textile#thecontext

[2]?robotlegs-best-practice(en):https://github.com/robotlegs/robotlegs-framework/wiki/Best-Practices#wiki-thecontext

[3]?ActionScript Developer's Guide to Robotlegs

[4]?Robotlegs?進階開發(fā)筆記,http://www.wersling.com/?p=675

[5]?lash原生機制與as3signal的性能測試對比(http://alecmce.com/as3/events-and-signals-performance-tests

[6]?多模塊開發(fā),http://joelhooks.com/2010/05/02/modular-robotlegs/

[7]?eidiot寫的Robotlegs幻燈片,http://eidiot.net/2010/03/28/slides-of-my-robotlegs-presentation-today/

[8]?Flash務(wù)實主義——Flash中的MVC,http://uh.9ria.com/space.php?uid=12147&do=blog&id=9102

轉(zhuǎn)自作者:吳秦

出處:http://www.cnblogs.com/skynet/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 概念 StrangeIoc 是依據(jù)控制反轉(zhuǎn)和解耦原理設(shè)計的,支持依賴注入。 控制反轉(zhuǎn)即Ioc(Inversion ...
    葉小健閱讀 6,082評論 0 18
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,810評論 25 709
  • 櫻花系列集客拉鏈本 當手帳遇上櫻花,一場浪漫的邂逅即將開始。櫻花拉鏈本,讓你旅行更安心。把櫻花留在手帳里永不凋零...
    ee8fe3cbcf8f閱讀 166評論 0 0
  • 今天藥劑課上講乳劑,提到了脂肪乳,我見過這個東西,腦子一下子回到大概八年前,我連日子都記不清了,真是可笑,那個時候...
    董航阿姨閱讀 644評論 1 1
  • 周家 “啊,我終于回來了!”憶言言一下車就興奮地大叫。這可把周梓程下了一跳,不滿地說:“又不是沒見過!大驚...
    慕櫻吶閱讀 440評論 0 2

友情鏈接更多精彩內(nèi)容