# 實現對象的復用——享元模式(五)
14.5 單純享元模式和復合享元模式
標準的享元模式結構圖中既包含可以共享的具體享元類,也包含不可以共享的非共享具體享元類。但是在實際使用過程中,我們有時候會用到兩種特殊的享元模式:單純享元模式和復合享元模式,下面將對這兩種特殊的享元模式進行簡單的介紹:
1.單純享元模式
在單純享元模式中,所有的具體享元類都是可以共享的,不存在非共享具體享元類。單純享元模式的結構如圖14-6所示:

圖14-6 單純享元模式結構圖
2.復合享元模式
將一些單純享元對象使用組合模式加以組合,還可以形成復合享元對象,這樣的復合享元對象本身不能共享,但是它們可以分解成單純享元對象,而后者則可以共享。復合享元模式的結構如圖14-7所示:

圖14-7 復合享元模式結構圖
通過復合享元模式,可以確保復合享元類CompositeConcreteFlyweight中所包含的每個單純享元類ConcreteFlyweight都具有相同的外部狀態,而這些單純享元的內部狀態往往可以不同。如果希望為多個內部狀態不同的享元對象設置相同的外部狀態,可以考慮使用復合享元模式。
14.6 關于享元模式的幾點補充
1.與其他模式的聯用
享元模式通常需要和其他模式一起聯用,幾種常見的聯用方式如下:
(1)在享元模式的享元工廠類中通常提供一個靜態的工廠方法用于返回享元對象,使用簡單工廠模式來生成享元對象。
(2)在一個系統中,通常只有唯一一個享元工廠,因此可以使用單例模式進行享元工廠類的設計。
(3)享元模式可以結合組合模式形成復合享元模式,統一對多個享元對象設置外部狀態。
2.享元模式與String類
JDK類庫中的String類使用了享元模式,我們通過如下代碼來加以說明:
```
class Demo {
public static void main(String args[]) {
String str1 = "abcd";
String str2 = "abcd";
String str3 = "ab" + "cd";
String str4 = "ab";
str4 += "cd";
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1 == str4);
str2 += "e";
System.out.println(str1 == str2);
}
}
```
在Java語言中,如果每次執行類似String str1="abcd"的操作時都創建一個新的字符串對象將導致內存開銷很大,因此如果第一次創建了內容為"abcd"的字符串對象str1,下一次再創建內容相同的字符串對象str2時會將它的引用指向"abcd",不會重新分配內存空間,從而實現了"abcd"在內存中的共享。上述代碼輸出結果如下:
```
true
true
false
false
```
可以看出,前兩個輸出語句均為true,說明str1、str2、str3在內存中引用了相同的對象;如果有一個字符串str4,其初值為"ab",再對它進行操作str4 += "cd",此時雖然str4的內容與str1相同,但是由于str4的初始值不同,在創建str4時重新分配了內存,所以第三個輸出語句結果為false;最后一個輸出語句結果也為false,說明當對str2進行修改時將創建一個新的對象,修改工作在新對象上完成,而原來引用的對象并沒有發生任何改變,str1仍然引用原有對象,而str2引用新對象,str1與str2引用了兩個完全不同的對象。
擴展
> 關于Java String類這種在修改享元對象時,先將原有對象復制一份,然后在新對象上再實施修改操作的機制稱為“Copy On Write”,大家可以自行查詢相關資料來進一步了解和學習“Copy On Write”機制,在此不作詳細說明。
14.7 享元模式總結
當系統中存在大量相同或者相似的對象時,享元模式是一種較好的解決方案,它通過共享技術實現相同或相似的細粒度對象的復用,從而節約了內存空間,提高了系統性能。相比其他結構型設計模式,享元模式的使用頻率并不算太高,但是作為一種以“節約內存,提高性能”為出發點的設計模式,它在軟件開發中還是得到了一定程度的應用。
1.主要優點
享元模式的主要優點如下:
(1) 可以極大減少內存中對象的數量,使得相同或相似對象在內存中只保存一份,從而可以節約系統資源,提高系統性能。
(2) 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享。
2.主要缺點
享元模式的主要缺點如下:
(1) 享元模式使得系統變得復雜,需要分離出內部狀態和外部狀態,這使得程序的邏輯復雜化。
(2) 為了使對象可以共享,享元模式需要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長。
3.適用場景
在以下情況下可以考慮使用享元模式:
(1) 一個系統有大量相同或者相似的對象,造成內存的大量耗費。
(2) 對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中。
(3) 在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統資源,因此,應當在需要多次重復使用享元對象時才值得使用享元模式。
練習
> Sunny軟件公司欲開發一個多功能文檔編輯器,在文本文檔中可以插入圖片、動畫、視頻等多媒體資料,為了節約系統資源,相同的圖片、動畫和視頻在同一個文檔中只需保存一份,但是可以多次重復出現,而且它們每次出現時位置和大小均可不同。試使用享元模式設計該文檔編輯器。
- Introduction
- 基礎知識
- 設計模式概述
- 從招式與內功談起——設計模式概述(一)
- 從招式與內功談起——設計模式概述(二)
- 從招式與內功談起——設計模式概述(三)
- 面向對象設計原則
- 面向對象設計原則之單一職責原則
- 面向對象設計原則之開閉原則
- 面向對象設計原則之里氏代換原則
- 面向對象設計原則之依賴倒轉原則
- 面向對象設計原則之接口隔離原則
- 面向對象設計原則之合成復用原則
- 面向對象設計原則之迪米特法則
- 六個創建型模式
- 簡單工廠模式-Simple Factory Pattern
- 工廠三兄弟之簡單工廠模式(一)
- 工廠三兄弟之簡單工廠模式(二)
- 工廠三兄弟之簡單工廠模式(三)
- 工廠三兄弟之簡單工廠模式(四)
- 工廠方法模式-Factory Method Pattern
- 工廠三兄弟之工廠方法模式(一)
- 工廠三兄弟之工廠方法模式(二)
- 工廠三兄弟之工廠方法模式(三)
- 工廠三兄弟之工廠方法模式(四)
- 抽象工廠模式-Abstract Factory Pattern
- 工廠三兄弟之抽象工廠模式(一)
- 工廠三兄弟之抽象工廠模式(二)
- 工廠三兄弟之抽象工廠模式(三)
- 工廠三兄弟之抽象工廠模式(四)
- 工廠三兄弟之抽象工廠模式(五)
- 單例模式-Singleton Pattern
- 確保對象的唯一性——單例模式 (一)
- 確保對象的唯一性——單例模式 (二)
- 確保對象的唯一性——單例模式 (三)
- 確保對象的唯一性——單例模式 (四)
- 確保對象的唯一性——單例模式 (五)
- 原型模式-Prototype Pattern
- 對象的克隆——原型模式(一)
- 對象的克隆——原型模式(二)
- 對象的克隆——原型模式(三)
- 對象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 復雜對象的組裝與創建——建造者模式(一)
- 復雜對象的組裝與創建——建造者模式(二)
- 復雜對象的組裝與創建——建造者模式(三)
- 七個結構型模式
- 適配器模式-Adapter Pattern
- 不兼容結構的協調——適配器模式(一)
- 不兼容結構的協調——適配器模式(二)
- 不兼容結構的協調——適配器模式(三)
- 不兼容結構的協調——適配器模式(四)
- 橋接模式-Bridge Pattern
- 處理多維度變化——橋接模式(一)
- 處理多維度變化——橋接模式(二)
- 處理多維度變化——橋接模式(三)
- 處理多維度變化——橋接模式(四)
- 組合模式-Composite Pattern
- 樹形結構的處理——組合模式(一)
- 樹形結構的處理——組合模式(二)
- 樹形結構的處理——組合模式(三)
- 樹形結構的處理——組合模式(四)
- 樹形結構的處理——組合模式(五)
- 裝飾模式-Decorator Pattern
- 擴展系統功能——裝飾模式(一)
- 擴展系統功能——裝飾模式(二)
- 擴展系統功能——裝飾模式(三)
- 擴展系統功能——裝飾模式(四)
- 外觀模式-Facade Pattern
- 深入淺出外觀模式(一)
- 深入淺出外觀模式(二)
- 深入淺出外觀模式(三)
- 享元模式-Flyweight Pattern
- 實現對象的復用——享元模式(一)
- 實現對象的復用——享元模式(二)
- 實現對象的復用——享元模式(三)
- 實現對象的復用——享元模式(四)
- 實現對象的復用——享元模式(五)
- 代理模式-Proxy Pattern
- 設計模式之代理模式(一)
- 設計模式之代理模式(二)
- 設計模式之代理模式(三)
- 設計模式之代理模式(四)
- 十一個行為型模式
- 職責鏈模式-Chain of Responsibility Pattern
- 請求的鏈式處理——職責鏈模式(一)
- 請求的鏈式處理——職責鏈模式(二)
- 請求的鏈式處理——職責鏈模式(三)
- 請求的鏈式處理——職責鏈模式(四)
- 命令模式-Command Pattern
- 請求發送者與接收者解耦——命令模式(一)
- 請求發送者與接收者解耦——命令模式(二)
- 請求發送者與接收者解耦——命令模式(三)
- 請求發送者與接收者解耦——命令模式(四)
- 請求發送者與接收者解耦——命令模式(五)
- 請求發送者與接收者解耦——命令模式(六)
- 解釋器模式-Interpreter Pattern
- 自定義語言的實現——解釋器模式(一)
- 自定義語言的實現——解釋器模式(二)
- 自定義語言的實現——解釋器模式(三)
- 自定義語言的實現——解釋器模式(四)
- 自定義語言的實現——解釋器模式(五)
- 自定義語言的實現——解釋器模式(六)
- 迭代器模式-Iterator Pattern
- 遍歷聚合對象中的元素——迭代器模式(一)
- 遍歷聚合對象中的元素——迭代器模式(二)
- 遍歷聚合對象中的元素——迭代器模式(三)
- 遍歷聚合對象中的元素——迭代器模式(四)
- 遍歷聚合對象中的元素——迭代器模式(五)
- 遍歷聚合對象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 協調多個對象之間的交互——中介者模式(一)
- 協調多個對象之間的交互——中介者模式(二)
- 協調多個對象之間的交互——中介者模式(三)
- 協調多個對象之間的交互——中介者模式(四)
- 協調多個對象之間的交互——中介者模式(五)
- 備忘錄模式-Memento Pattern
- 撤銷功能的實現——備忘錄模式(一)
- 撤銷功能的實現——備忘錄模式(二)
- 撤銷功能的實現——備忘錄模式(三)
- 撤銷功能的實現——備忘錄模式(四)
- 撤銷功能的實現——備忘錄模式(五)
- 觀察者模式-Observer Pattern
- 對象間的聯動——觀察者模式(一)
- 對象間的聯動——觀察者模式(二)
- 對象間的聯動——觀察者模式(三)
- 對象間的聯動——觀察者模式(四)
- 對象間的聯動——觀察者模式(五)
- 對象間的聯動——觀察者模式(六)
- 狀態模式-State Pattern
- 處理對象的多種狀態及其相互轉換——狀態模式(一)
- 處理對象的多種狀態及其相互轉換——狀態模式(二)
- 處理對象的多種狀態及其相互轉換——狀態模式(三)
- 處理對象的多種狀態及其相互轉換——狀態模式(四)
- 處理對象的多種狀態及其相互轉換——狀態模式(五)
- 處理對象的多種狀態及其相互轉換——狀態模式(六)
- 策略模式-Strategy Pattern
- 算法的封裝與切換——策略模式(一)
- 算法的封裝與切換——策略模式(二)
- 算法的封裝與切換——策略模式(三)
- 算法的封裝與切換——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 訪問者模式-Visitor Pattern
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)