享元模式(Flyweight Pattern):運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用。系統(tǒng)只使用少量的對(duì)象,而這些對(duì)象都很相似,狀態(tài)變化很小龙宏,可以實(shí)現(xiàn)對(duì)象的多次復(fù)用。由于享元模式要求能夠共享的對(duì)象必須是細(xì)粒度對(duì)象伤疙,因此它又稱為輕量級(jí)模式银酗,它是一種對(duì)象結(jié)構(gòu)型模式。
享元模式從字面上翻譯是“蠅量級(jí)模式”徒像,其實(shí)并不太好理解黍特。這個(gè)模式的作用就是在一個(gè)系統(tǒng)當(dāng)中有很多很多的對(duì)象,而這些對(duì)象很相似锯蛀,有細(xì)微地方不同灭衷,單數(shù)數(shù)量太大,影響系統(tǒng)性能旁涤,為了避免系統(tǒng)中出現(xiàn)大量相同或相似的對(duì)象翔曲,同時(shí)又不影響客戶端程序通過(guò)面向?qū)ο蟮姆绞綄?duì)這些對(duì)象進(jìn)行操作,享元模式橫空出世拭抬。
好比一個(gè)系統(tǒng)里面的字符,有非常之多侵蒙,每個(gè)字符在顯示的時(shí)候有的顏色不一樣造虎,有的大小不一樣,有的字體不一樣纷闺,享元模式通過(guò)共享技術(shù)實(shí)現(xiàn)相同或相似對(duì)象的重用算凿,在邏輯上每一個(gè)出現(xiàn)的字符都有一個(gè)對(duì)象與之對(duì)應(yīng),然而在物理上它們卻共享同一個(gè)享元對(duì)象犁功,這個(gè)對(duì)象可以出現(xiàn)在一個(gè)字符串的不同地方氓轰,相同的字符對(duì)象都指向同一個(gè)實(shí)例。而享元模式里面一個(gè)重要的地方是享元池浸卦,里面裝滿了各種享元對(duì)象署鸡。這里可以看出享元模式的“享”是共享,“元”代表公共對(duì)象的最原始的狀態(tài)限嫌。
先來(lái)看下享元模式的類圖:
-
Flyweight
(抽象享元類):通常是一個(gè)接口或抽象類靴庆,在抽象享元類中聲明了具體享元類公共的方法,這些方法可以向外界提供享元對(duì)象的內(nèi)部數(shù)據(jù)(內(nèi)部狀態(tài))怒医,同時(shí)也可以通過(guò)這些方法來(lái)設(shè)置外部數(shù)據(jù)(外部狀態(tài))炉抒。 -
ConcreteFlyweight
(具體享元類):它實(shí)現(xiàn)了抽象享元類,其實(shí)例稱為享元對(duì)象
稚叹;在具體享元類中為內(nèi)部狀態(tài)提供了存儲(chǔ)空間焰薄。通常我們可以結(jié)合單例模式來(lái)設(shè)計(jì)具體享元類拿诸,為每一個(gè)具體享元類提供唯一的享元對(duì)象。 -
UnsharedConcreteFlyweight
(非共享具體享元類):并不是所有的抽象享元類的子類都需要被共享塞茅,不能被共享的子類可設(shè)計(jì)為非共享具體享元類亩码;當(dāng)需要一個(gè)非共享具體享元類的對(duì)象時(shí)可以直接通過(guò)實(shí)例化創(chuàng)建。 -
FlyweightFactory
(享元工廠類):享元工廠類用于創(chuàng)建并管理享元對(duì)象凡桥,它針對(duì)抽象享元類編程蟀伸,將各種類型的具體享元對(duì)象存儲(chǔ)在一個(gè)享元池中,享元池一般設(shè)計(jì)為一個(gè)存儲(chǔ)“鍵值對(duì)”的集合(也可以是其他類型的集合)缅刽,可以結(jié)合工廠模式進(jìn)行設(shè)計(jì)啊掏;當(dāng)用戶請(qǐng)求一個(gè)具體享元對(duì)象時(shí),享元工廠提供一個(gè)存儲(chǔ)在享元池中已創(chuàng)建的實(shí)例或者創(chuàng)建一個(gè)新的實(shí)例(如果不存在的話)衰猛,返回新創(chuàng)建的實(shí)例并將其存儲(chǔ)在享元池中迟蜜。
代碼實(shí)現(xiàn)下:
享元模式的抽象接口
public abstract class Flyweight {
public abstract void operation(String state);
}
具體的享元類
public class ConcreteFlyweight extends Flyweight{
private Character intrinsicState=null;
//構(gòu)造函數(shù),設(shè)置內(nèi)部狀態(tài)
public ConcreteFlyweight(Character intrinsicState) {
this.intrinsicState=intrinsicState;
}
//外部狀態(tài)作為參數(shù)進(jìn)入方法
@Override
public void operation(String extrinsicState) {
System.out.println("內(nèi)部狀態(tài)是:"+intrinsicState);
System.out.println("外部狀態(tài)是:"+ extrinsicState);
}
}
不需要共享的情況啡省,有時(shí)候就是需要在內(nèi)存里面新增一個(gè)
public class UnsharedConcreteFlyweight extends Flyweight{
@Override
public void operation(String extrinsicstate) {
System.out.println("不共享的具體狀態(tài):"+extrinsicstate);
}
}
接下來(lái)是享元模式的工廠娜睛,負(fù)責(zé)整合這些“元”,由一個(gè) Hash 表進(jìn)行管理,在構(gòu)造的時(shí)候沒(méi)有把不共享的“元”增加進(jìn)去卦睹,當(dāng)然也可以進(jìn)行判斷畦戒。
public class FlyweightFactory {
private HashMap<String,Flyweight> characters=new HashMap<String,Flyweight>();
public FlyweightFactory() {
characters.put("A", new ConcreteFlyweight('a'));
characters.put("B", new ConcreteFlyweight('b'));
characters.put("C", new ConcreteFlyweight('c'));
}
public Flyweight getFlyweight(String key){
return characters.get(key);
}
}
Client 端將通過(guò)工廠從里面獲取,工廠一般可以做成單例结序,確保系統(tǒng)范圍只有一個(gè)享元工廠障斋。
public class Client {
public static void main(String[] args) {
FlyweightFactory factory=new FlyweightFactory();
Flyweight a1=factory.getFlyweight("A");
a1.operation("StateA1");
Flyweight a2=factory.getFlyweight("A");
a2.operation("StateA2");
Flyweight b=factory.getFlyweight("B");
b.operation("StateB");
}
}
看下結(jié)果:
內(nèi)部狀態(tài)是:a
外部狀態(tài)是:StateA1
內(nèi)部狀態(tài)是:a
外部狀態(tài)是:StateA2
內(nèi)部狀態(tài)是:b
外部狀態(tài)是:StateB
擴(kuò)展:有的時(shí)候需要把幾個(gè) Flyweight 組合為一個(gè),這就出現(xiàn)了具體復(fù)合享元角色徐鹤,他是有一些單個(gè)的享元角色組合而成垃环,提供一個(gè)集合進(jìn)行管理,當(dāng)然也繼承抽象的享元角色返敬,也有 operation()方法遂庄,這個(gè)方法的唯一參數(shù)代表復(fù)合對(duì)象的外部狀態(tài),而且這個(gè)外部狀態(tài)和內(nèi)部包括的小享元的外部狀態(tài)一致劲赠,內(nèi)部狀態(tài)不一定一樣涛目。由于內(nèi)部狀態(tài)會(huì)改變,所以也是不能共享的凛澎。
優(yōu)點(diǎn)和缺點(diǎn)
享元模式的主要優(yōu)點(diǎn)如下:
(1) 可以極大減少內(nèi)存中對(duì)象的數(shù)量泌绣,使得相同或相似對(duì)象在內(nèi)存中只保存一份,從而可以節(jié)約系統(tǒng)資源预厌,提高系統(tǒng)性能阿迈。
(2) 享元模式的外部狀態(tài)相對(duì)獨(dú)立,而且不會(huì)影響其內(nèi)部狀態(tài)轧叽,從而使得享元對(duì)象可以在不同的環(huán)境中被共享苗沧。
享元模式的主要缺點(diǎn)如下:
(1) 享元模式使得系統(tǒng)變得復(fù)雜刊棕,需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化待逞。
(2) 為了使對(duì)象可以共享甥角,享元模式需要將享元對(duì)象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使得運(yùn)行時(shí)間變長(zhǎng)识樱。
該模式使用頻率不高嗤无,場(chǎng)合不多,在以下情況下可以考慮使用享元模式:
(1) 一個(gè)系統(tǒng)有大量相同或者相似的對(duì)象怜庸,造成內(nèi)存的大量耗費(fèi)当犯。
(2) 對(duì)象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對(duì)象中割疾。
(3) 在使用享元模式時(shí)需要維護(hù)一個(gè)存儲(chǔ)享元對(duì)象的享元池嚎卫,而這需要耗費(fèi)一定的系統(tǒng)資源,因此宏榕,應(yīng)當(dāng)在需要多次重復(fù)使用享元對(duì)象時(shí)才值得使用享元模式拓诸。