Flyweight模式產(chǎn)生緣由:
在面向?qū)ο笙到y(tǒng)的設(shè)計(jì)何實(shí)現(xiàn)中,創(chuàng)建對(duì)象是最為常見(jiàn)的操作。這里面就有1個(gè)問(wèn)題:如果1個(gè)利用程序使用了太多的對(duì)象,就會(huì)造成很大的存儲(chǔ)開(kāi)消。特別是對(duì)大量輕量級(jí)(細(xì)粒度)的對(duì)象,比如在文檔編輯器的設(shè)計(jì)進(jìn)程中,我們?nèi)绻麨闆](méi)有字母創(chuàng)建1個(gè)對(duì)象的話(huà),系統(tǒng)可能會(huì)由于大量的對(duì)象而造成存儲(chǔ)開(kāi)消的浪費(fèi)。例如1個(gè)字母“a”在文檔中出現(xiàn)了100000次,而實(shí)際上我們可讓這1萬(wàn)個(gè)字母“a”同享1個(gè)對(duì)象,固然由于在不同的位置可能字母“a”有不同的顯示效果(例如字體和大小等設(shè)置不同),在這類(lèi)情況我們可以為將對(duì)象的狀態(tài)分為“外部狀態(tài)”和“內(nèi)部狀態(tài)”,將可以被同享(不會(huì)變化)的狀態(tài)作為內(nèi)部狀態(tài)存儲(chǔ)在對(duì)象中,而外部對(duì)象(例如上面提到的字體、大小等)我們可以在適當(dāng)?shù)臅r(shí)候?qū)⑼獠繉?duì)象最為參數(shù)傳遞給對(duì)象(例如在顯示的時(shí)候,將字體、大小等信息傳遞給對(duì)象)。
Flyweight享元模式作用:
Flyweight模式是1個(gè)提高程序效力和性能的模式,會(huì)大大加快程序的運(yùn)行速度。利用場(chǎng)合很多:比如你要從1個(gè)數(shù)據(jù)庫(kù)中讀取1系列字符串,這些字符串中有許多是重復(fù)的,那末我們可以將這些字符串貯存在Flyweight池(pool)中。就是先創(chuàng)建1個(gè)的原始模型,然后隨著不同場(chǎng)合和環(huán)境,再產(chǎn)生各具特點(diǎn)的具體模型,很明顯,在這里需要產(chǎn)生不同的新對(duì)象,所以Flyweight模式中常出現(xiàn)Factory模式。Flyweight的內(nèi)部狀態(tài)是用來(lái)同享的,F(xiàn)lyweight factory負(fù)責(zé)保護(hù)1個(gè)Flyweightpool(模式池)來(lái)寄存內(nèi)部狀態(tài)的對(duì)象。
Flyweight享元模式的使用處景:
當(dāng)以下所有的條件都滿(mǎn)足時(shí),可以斟酌使用享元模式:
(1).1個(gè)系統(tǒng)有大量的對(duì)象。
(2).這些對(duì)象耗費(fèi)大量的內(nèi)存。
(3).這些對(duì)象的狀態(tài)中的大部份都可之外部化。
(4).這些對(duì)象可以依照內(nèi)蘊(yùn)狀態(tài)分成很多的組,當(dāng)把外蘊(yùn)對(duì)象從對(duì)象中剔除時(shí),每個(gè)組都可以?xún)H用1個(gè)對(duì)象代替。
(5).軟件系統(tǒng)不依賴(lài)于這些對(duì)象的身份,換言之,這些對(duì)象可以是不可分辨的。
滿(mǎn)足以上的這些條件的系統(tǒng)可使用享元對(duì)象。
最后,使用享元模式需要保護(hù)1個(gè)記錄了系統(tǒng)已有的所有享元的表,而這需要耗費(fèi)資源。因此,應(yīng)當(dāng)在有足夠多的享元實(shí)例可供同享時(shí)才值得使用享元模式。
單純Flyweight享元模式模式典型的UML結(jié)構(gòu)圖如圖1所示:
單純Flyweight享元模式抽象基類(lèi)及接口:
抽象享元(Flyweight)角色:此角色是所有的具體享元類(lèi)的超類(lèi),為這些類(lèi)規(guī)定出需要實(shí)現(xiàn)的公共接口。那些需要外蘊(yùn)狀態(tài)(External State)的操作可以通過(guò)調(diào)用商業(yè)方法以參數(shù)情勢(shì)傳入。
具體享元(ConcreteFlyweight)角色:實(shí)現(xiàn)抽象享元角色所規(guī)定的接口。如果有內(nèi)蘊(yùn)狀態(tài)的話(huà),必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供存儲(chǔ)空間。享元對(duì)象的內(nèi)蘊(yùn)狀態(tài)必須與對(duì)象所處的周?chē)h(huán)境無(wú)關(guān),從而使得享元對(duì)象可以在系統(tǒng)內(nèi)同享的。
享元工廠(FlyweightFactory)角色:本角色負(fù)責(zé)創(chuàng)建和管理享元角色。本角色必須保證享元對(duì)象可以被系統(tǒng)適當(dāng)?shù)赝怼.?dāng)1個(gè)客戶(hù)端對(duì)象調(diào)用1個(gè)享元對(duì)象的時(shí)候,享元工廠角色會(huì)檢查系統(tǒng)中是不是已有1個(gè)復(fù)合要求的享元對(duì)象。如果已有了,享元工廠角色就應(yīng)當(dāng)提供這個(gè)已有的享元對(duì)象;如果系統(tǒng)中沒(méi)有1個(gè)適當(dāng)?shù)南碓獙?duì)象的話(huà),享元工廠角色就應(yīng) 當(dāng)創(chuàng)建1個(gè)適合的享元對(duì)象。
客戶(hù)端(Client)角色:本角色需要保護(hù)1個(gè)對(duì)所有享元對(duì)象的援用。本角色需要自行存儲(chǔ)所有享元對(duì)象的外蘊(yùn)狀態(tài)。
單純Flyweight享元模式典型的示例代碼以下:
復(fù)合Flyweight享元模式抽象基類(lèi)及接口:
抽象享元角色:此角色是所有的具體享元類(lèi)的超類(lèi),為這些類(lèi)規(guī)定出需要實(shí)現(xiàn)的公共接口。那些需要外蘊(yùn)狀態(tài)(External State)的操作可以通過(guò)方法的參數(shù)傳入。抽象享元的接口使得享元變得可能,但是其實(shí)不強(qiáng)迫子類(lèi)實(shí)行同享,因此并不是所有的享元對(duì)象都是可以同享的。
具體享元(ConcreteFlyweight)角色:實(shí)現(xiàn)抽象享元角色所規(guī)定的接口。如果有內(nèi)蘊(yùn)狀態(tài)的話(huà),必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供存儲(chǔ)空間。享元對(duì)象的內(nèi)蘊(yùn)狀態(tài)必須與對(duì)象所處的周?chē)h(huán)境無(wú)關(guān),從而使得享元對(duì)象可以在系統(tǒng)內(nèi)同享。有時(shí)候具體享元角色又叫做單純具體享元角色,由于復(fù)合享元角色是由單純具體享元角色通過(guò)復(fù)合而成的。
復(fù)合享元(UnsharableFlyweight)角色:復(fù)合享元角色所代表的對(duì)象是不可以同享的,但是1個(gè)復(fù)合享元對(duì)象可以分解成為多個(gè)本身是單純享元對(duì)象的組合。復(fù)合享元角色又稱(chēng)做不可同享的享元對(duì)象。
享元工廠(FlyweightFactoiy)角色:本角色負(fù)責(zé)創(chuàng)建和管理享元角色。本角色必須保證享元對(duì)象可以被系統(tǒng)適當(dāng)?shù)赝?。?dāng)1個(gè)客戶(hù)端對(duì)象要求1個(gè)享元對(duì)象的時(shí)候,享元工廠角色需要檢查系統(tǒng)中是不是已有1個(gè)符合要求的享元對(duì)象,如果已有了,享元工廠角色就應(yīng)當(dāng)提供這個(gè)已有的享元對(duì)象;如果系統(tǒng)中沒(méi)有1個(gè)適當(dāng)?shù)南碓獙?duì)象的話(huà),享元工廠角色就應(yīng)當(dāng)創(chuàng)建1個(gè)新的適合的享元對(duì)象。
客戶(hù)端(Client)角色:本角色還需要自行存儲(chǔ)所有享元對(duì)象的外蘊(yùn)狀態(tài)。
示例1:1個(gè)咖啡的例子
在這個(gè)咖啡攤(CoffeeStall)所使用的系統(tǒng)里,有1系列的咖啡"風(fēng)味(Flavor)"。客人到攤位上購(gòu)買(mǎi)咖啡,所有的咖啡均放在臺(tái)子上,客人自己拿到咖啡后就離開(kāi)攤位??Х扔袃?nèi)蘊(yùn)狀態(tài),也就是咖啡的風(fēng)味;咖啡沒(méi)有環(huán)境因素,也就是說(shuō)沒(méi)有外蘊(yùn)狀態(tài)。如果系統(tǒng)為每杯咖啡都創(chuàng)建1個(gè)獨(dú)立的對(duì)象的話(huà),那末就需要?jiǎng)?chuàng)建出很多的細(xì)小對(duì)象來(lái)。這樣就不如把咖啡依照種類(lèi)(即"風(fēng)味")劃分,每種風(fēng)味的咖啡只創(chuàng)建1個(gè)對(duì)象,并實(shí)行同享。
使用咖啡攤主的語(yǔ)言來(lái)說(shuō),所有的咖啡都可按"風(fēng)味"劃分成如Capucino、Espresso等,每種風(fēng)味的咖啡不論賣(mài)出多少杯,都是全同、不可分辨的。所謂同享,就是咖啡風(fēng)味的同享,制造方法的同享等。因此,享元模式對(duì)咖啡攤來(lái)講,就意味著不需要為每份單獨(dú)調(diào)制。攤主可以在需要時(shí),1次性地調(diào)制出足夠1天出售的某1種風(fēng)味的咖啡。
很明顯,這里合適使用單純享元模式。系統(tǒng)的設(shè)計(jì)以下:
示例2:咖啡店的例子
在前面的咖啡攤項(xiàng)目里,由于沒(méi)有供客人坐的桌子,所有的咖啡均沒(méi)有環(huán)境的影響。換言之,咖啡唯一內(nèi)蘊(yùn)狀態(tài),也就是咖啡的種類(lèi),而沒(méi)有外蘊(yùn)狀態(tài)。下面斟酌1個(gè)范圍稍稍大1點(diǎn)的咖啡屋(Coffee Shop)項(xiàng)目。屋子里有很多的桌子供客人坐,系統(tǒng)除需要提供咖啡的"風(fēng)味"以外,還需要跟蹤咖啡被送到哪個(gè)桌位上,因此,咖啡就有了桌子作為外蘊(yùn)狀態(tài)。
由于外蘊(yùn)狀態(tài)的存在,沒(méi)有外蘊(yùn)狀態(tài)的單純享元模式不再符合要求。系統(tǒng)的設(shè)計(jì)可以利用有外蘊(yùn)狀態(tài)的單純享元模式。系統(tǒng)的代碼以下:
Flyweight享元模式使用總結(jié):
(1).Flyweight享元模式的優(yōu)點(diǎn)在于它大幅度地下降內(nèi)存中對(duì)象的數(shù)量。但是,它做到這1點(diǎn)所付出的代價(jià)也是很高的:
(2).Flyweight享元模式使得系統(tǒng)更加復(fù)雜。為了使對(duì)象可以同享,需要將1些狀態(tài)外部化,這使得程序的邏輯復(fù)雜化。
(3).Flyweight享元模式將享元對(duì)象的狀態(tài)外部化,而讀取外部狀態(tài)使得運(yùn)行時(shí)間略微變長(zhǎng)。