詳解Java設(shè)計(jì)模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)
享元(Flyweight)模式:通過共享技術(shù)以便有效的支持大量細(xì)粒度的對象。
享元模式在閻宏的《java與模式》中分為單純享元模式和復(fù)合享元模式,復(fù)合模式的復(fù)合享元是不可以共享的,享元對象能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊(yùn)態(tài)(Internal State)和外蘊(yùn)態(tài)( External State)。這兩個(gè)“蘊(yùn)態(tài)”翻譯的太難懂,我不是說翻譯的不好,可能是我理解能力差,還是《Design Pattern Elements of Reusable Object-Oriented Software》的翻譯版《設(shè)計(jì)模式可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書總翻譯為內(nèi)部對象和外部對象,相對直白,對概念性的東西文學(xué)氣味太強(qiáng)了就覺得很別扭。這里的角色也采用《設(shè)計(jì)模式可復(fù)用面向?qū)ο筌浖幕A(chǔ)》的說法,不區(qū)分單純模式和復(fù)合模式,而是有一個(gè)UnSharedConcreteFlyweight(在《java與模式》里稱復(fù)合享元,指明復(fù)合享元不能共享),我們這里稱它不可以共享享元角色,這樣享元模式的角色有:
- 抽象享元(Flyweight)角色:是給實(shí)現(xiàn)享元提供的接口。
- 具體享元(ConcreteFlyweight)角色:實(shí)現(xiàn)抽象角色,此對象必須是共享的,所含的狀態(tài)必須是內(nèi)部狀態(tài)。
- 不共享享元(UnSharedConcreteFlyweight)角色:此對象不可共享,不是所有實(shí)現(xiàn)抽象享元接口的的對象都要共享,此對象通常將ConcreteFlyweight作為組成元素。
- 享元工廠(FlyweightFactory)角色:負(fù)責(zé)創(chuàng)建和管理享元角色,確保合理共享。
- 客戶端(Client)角色:維持一個(gè)Flyweight對象的引用,計(jì)算或存儲(chǔ)一個(gè)(多個(gè))外部存儲(chǔ)狀態(tài)。
享元模式的類的機(jī)構(gòu)圖如下:

享元模式在java.lang.String設(shè)計(jì)上的使用,我們知道java中字符串始終保持共享一份,如下面代碼片段:
String m = "a"; String n = "a"; System.out.println(m==n);
這樣會(huì)輸出true,說明m和n指向了同一個(gè)實(shí)例,內(nèi)存中也只有一個(gè)"a"。這就是享元模式在String上的使用。
享元模式在文字編輯存貯過程中的使用,這里假定文章由行對象組成,行對象由若干個(gè)字符對象組成,但是如果每個(gè)字符都保存自己的對象,那么一篇文章成千上萬個(gè)字符對象,這樣嚴(yán)重消耗系統(tǒng)內(nèi)存,造成不可接受的運(yùn)行時(shí)開銷,好的方法是利用享元模式,只保存ASCII字符編碼值,作為內(nèi)部不變的狀態(tài),對當(dāng)個(gè)字符對象進(jìn)行共享,而相對字符顏色、大小這樣的格式化數(shù)據(jù)作為外部狀態(tài),由客戶端維護(hù),運(yùn)行時(shí)由外部傳入即可。每個(gè)行作為不可共享享元對象,它是由享元對象(字符對象)組合而成的。

我們來看個(gè)簡單地享元模式的結(jié)構(gòu)的例子:
/**
* 字母
*/
public class Letter {
private String name;
public Letter(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
/**
* 一個(gè)產(chǎn)生字母對象的 享元工廠(單例工廠)
*/
public class LetterFactory {
private Map<String, Letter> map;
private static LetterFactory instance = new LetterFactory();
private LetterFactory() {
map = new HashMap<String, Letter>();
}
public static LetterFactory getInstance() {
return instance;
}
public void add(Letter letter) {
if (letter != null && !map.containsKey(letter.getName())) {
map.put(letter.getName(), letter);
}
System.out.println("map.size====" + map.size());
}
public Letter get(String name) {
return map.get(name);
}
}
public class Test {
public static void main(String[] args) {
LetterFactory factory = LetterFactory.getInstance();
String word = "easiness";
addLetterByName(factory, word);
getLetter(factory, word);
}
//添加字母對象
static void addLetterByName(LetterFactory factory, String word) {
for (char c : word.toCharArray()) {
factory.add(new Letter(c + ""));
}
}
//輸出字母對象
static void getLetter(LetterFactory factory, String word) {
for (char c : word.toCharArray()) {
System.out.println(factory.get(c + ""));
}
}
}
打?。?/p>
map.size====1 map.size====2 map.size====2 map.size====3 map.size====4 map.size====5 map.size====5 flyweight.Letter@3343c8b3 flyweight.Letter@272d7a10 flyweight.Letter@3343c8b3 flyweight.Letter@1aa8c488 flyweight.Letter@3dfeca64 flyweight.Letter@22998b08 flyweight.Letter@1aa8c488
相關(guān)文章
淺析RxJava處理復(fù)雜表單驗(yàn)證問題的方法
這篇文章主要介紹了RxJava處理復(fù)雜表單驗(yàn)證問題的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
Java使用String.format方法格式化字符串的示例詳解
在編程過程中,我們經(jīng)常需要?jiǎng)?chuàng)建格式化的字符串來滿足特定的需求,比如生成用戶友好的消息、構(gòu)建報(bào)告或是輸出調(diào)試信息,Java 提供了一個(gè)強(qiáng)大的工具——String.format 方法,本文給大家介紹了Java使用String.format方法格式化字符串的示例,需要的朋友可以參考下2024-11-11
如何實(shí)現(xiàn)在IDEA中導(dǎo)入一個(gè)模塊
這篇文章主要介紹了如何實(shí)現(xiàn)在IDEA中導(dǎo)入一個(gè)模塊方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Java動(dòng)態(tài)規(guī)劃方式解決不同的二叉搜索樹
二叉搜索樹作為一個(gè)經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點(diǎn),同時(shí)查詢效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細(xì)講講二叉搜索樹的原理與實(shí)現(xiàn),需要的可以參考一下2022-10-10
java連接MySQL數(shù)據(jù)庫實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了java連接MySQL數(shù)據(jù)庫實(shí)現(xiàn)代碼,感興趣的小伙伴們可以參考一下2016-06-06
解決idea check out 切換分支時(shí)找不到需要的分支問題
這篇文章主要介紹了解決idea check out 切換分支時(shí)找不到需要的分支問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

