享元模式是一種很常用的思想,核心就是共用,剝離事物的內部狀態與外部狀態,本文對享元模式 FlyWeight進行了簡單介紹,並且給出了該模式的意圖,結構,並且介紹了享元模式的兩種應用方式單純享元模式與複合享元模式,並且給出來了Java的示例代碼。享元模式的特點,結構,使用場景,都可以在本文中找到。 ...
享元模式(FlyWeight) “享”取“共用”之意,“元”取“單元”之意。
![image_5c087c0a_b27 image_5c087c0a_b27](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093609691-379965780.png)
意圖
運用共用技術,有效的支持大量細粒度的對象。意圖解析
面向對象的程式設計中,一切皆是對象,這也就意味著系統的運行將會依賴大量的對象。 試想,如果對象的數量過多,勢必會增加系統負擔,導致運行的代價過高。 下麵看兩個小例子理解下 1.)有一首歌曲叫做《大舌頭》 其中有一句歌詞“說說說說 說你愛我 我我我我 說不出口”![image_5c087c0a_3c7e image_5c087c0a_3c7e](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093610830-1624057401.png)
![image_5c087c0a_1241 image_5c087c0a_1241](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093611924-854264630.png)
![image_5c087c0a_7394 image_5c087c0a_7394](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093612395-560542170.png)
![image_5c087c0a_149c image_5c087c0a_149c](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093613669-300758631.png)
- 是,因為全部都是漢字“你”
- 不是,因為儘管都是漢字“你”,但是他們的字體,顏色,字型大小,卻又明顯不同,所以不是同樣的
![image_5c087c0b_388b image_5c087c0b_388b](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093614066-1482369814.png)
小結
享元模式就是為了避免系統中出現大量相同或相似的對象,同時又不影響客戶端程式通過面向對象的方式對這些對象進行操作 享元模式通過共用技術,實現相同或相似對象的重用 比如文編編輯器讀取文本 在邏輯上每一個出現的字元都有一個對象與之對應,然而在物理上它們卻共用同一個享元對象 在享元模式中,存儲這些共用實例對象的地方通常叫做享元池(Flyweight Pool) 享元模式可以結合String的intern()方法一起進行理解 通過區分了內部狀態和外部狀態,就可以將相同內部狀態的對象存儲在池中,池中的對象可以實現共用 需要的時候將對象從池中取出,實現對象的復用 通過向取出的對象註入不同的外部狀態,進而得到一些列相似的對象 而這些看似各異的對象在記憶體中,僅僅存儲了一份,大大節省了空間,所以說很自然的命名為“flyweight”輕量級享元工廠
通過對意圖的認識,可以認為,享元模式其實就是對於“程式中會出現的大量重覆或者相似對象”的一種“重構” 當然,你應該是在設計之初就想到這個問題,而不是真的出現問題後再去真的重構 比如,你想要設計“字元”這種對象時,就應該考慮到他的“大量””重覆““相似”的特點 所以需要分析出字元的內部狀態,與外部狀態 上面也提到對於享元對象,通過享元池進行管理 對於池的管理通常使用工廠模式,藉助於工廠類對享元池進行管理 用戶需要對象時,通過工廠類獲取 工廠提供一個存儲在享元池中的已創建的對象實例,或者創建一個新的實例示例代碼
針對於上面的例子,漢字“你”作為內部狀態,可以進行共用 “顏色”作為外部狀態,由客戶端保存傳遞 創建字元類 Character、漢字字元類ChineseCharacter、顏色類Color以及工廠類CharacterFactory Color含有顏色屬性,通過構造方法設置,getter方法獲取package flyweight; public class Color { public String Color; public Color(String color) { this.Color = color; } public String getColor() { return Color; } }
Character 抽象的字元類,用於描述字元
package flyweight; public abstract class Character { public abstract String getValue(); public void display(Color color) { System.out.println("字元: " + getValue() + " ,顏色: " + color.getColor()); } }漢字字元類,為了簡化,直接設置value為漢字“你”
package flyweight; public class ChineseCharacter extends Character { @Override public String getValue() { return "你"; } }CharacterFactory字元工廠類 通過單例模式創建工廠 內部HashMap用於存儲字元,並且提供獲取方法 為了簡化程式,初始就創建了一個漢字字元“你”存儲於字元中
package flyweight; import java.util.HashMap; public class CharacterFactory { /** * 單例模式 餓漢式創建 */ private static CharacterFactory instance = new CharacterFactory(); /** * 使用HashMap管理享元池 */ private HashMap<String, Object> hm = new HashMap<>(); private CharacterFactory() { Character character = new ChineseCharacter(); hm.put("你", character); } /** * 單例全局訪問介面獲取工廠 */ public static CharacterFactory getInstance() { return instance; } /** * 根據key獲取池中的對象 */ public Character getCharacter(String key) { return (Character) hm.get(key); } }測試代碼
![image_5c087c0b_6037 image_5c087c0b_6037](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093614513-1100880847.png)
![image_5c087c0b_2137 image_5c087c0b_2137](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093614902-1084214569.png)
結構
將上面的示例轉換為標準的享元模式的名稱![image_5c087c0b_1edf image_5c087c0b_1edf](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093615334-456552500.png)
客戶端角色維護了對所有享元對象的引用
![image_5c087c0b_69dd image_5c087c0b_69dd](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093615729-611110664.png)
![image_5c087c0b_6b32 image_5c087c0b_6b32](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093616101-1423534050.png)
分類
單純享元模式
在上面的結構中,如果所有的ConcreteFlyWeight都可以被共用 也就是所有的FlyWeight子類都可以被共用,那就是所有的享元對象都可以被共用 這種形式被稱之為單純享元模式 單純享元代碼package flyweight.simple; public abstract class FlyWeight { /** * 抽象的業務邏輯方法,接受外部狀態作為參數 */ abstract public void operation(String outerState); }
package flyweight.simple; public class ConcreteFlyWeight extends FlyWeight { private String innerState = null; public ConcreteFlyWeight(String innerState) { this.innerState = innerState; } /** * 外部狀態作為參數傳遞 */ @Override public void operation(String outerState) { System.out.println("innerState = " + innerState + " outerState = " + outerState); } }
package flyweight.simple; import java.util.HashMap; public class FlyWeightFactory { /** * 單例模式 餓漢式創建 */ private static FlyWeightFactory instance = new FlyWeightFactory(); /** * 使用HashMap管理享元池 */ private HashMap<String, Object> hm = new HashMap<>(); private FlyWeightFactory() { } /** * 單例全局訪問介面獲取工廠 */ public static FlyWeightFactory getInstance() { return instance; } /** * 根據innerState獲取池中的對象 * 存在返回,不存在創建並返回 */ public FlyWeight getFylWeight(String innerState) { if(hm.containsKey(innerState)){ return (FlyWeight) hm.get(innerState); }else{ FlyWeight flyWeight = new ConcreteFlyWeight(innerState); hm.put(innerState,flyWeight); return flyWeight; } } }
package flyweight.simple; public class Test { public static void main(String[] args){ FlyWeightFactory flyWeightFactory = FlyWeightFactory.getInstance(); FlyWeight flyWeight1 = flyWeightFactory.getFylWeight("First"); FlyWeight flyWeight2 = flyWeightFactory.getFylWeight("Second"); FlyWeight flyWeight3 = flyWeightFactory.getFylWeight("First"); System.out.println(flyWeight1); System.out.println(flyWeight2); System.out.println(flyWeight3); System.out.println(); flyWeight1.operation("outer state XXX"); flyWeight2.operation("outer state YYY"); flyWeight3.operation("outer state ZZZ"); } }
![image_5c087c0b_75b6 image_5c087c0b_75b6](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093616501-2119859399.png)
複合享元模式
與單純享元模式對應的是複合享元模式 單純享元模式中,所有的享元對象都可以共用 複合享元模式中,則並不是所有的ConcreteFlyWeight都可以被共用 也就是說:不是所有的享元對象都可以被共用 實際上,並不是所有的FlyWeight子類都需要被共用 FlyWeight介面使的可以進行共用,但是沒有任何必要 強制必須共用 實踐中,UnsharedConcreteFlyWeight對象通常將ConcreteFlyWeight對象作為子節點![image_5c087c0b_4a4f image_5c087c0b_4a4f](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093616926-859746290.png)
複合享元角色,也就是不可共用的,也被稱為 不可共用的享元對象
但是一個複合享元對象可以分解為多個本身是單純享元對象的組合
這些單純的享元對象就又是可以共用的 複合享元代碼 將簡單模式中的示例代碼進行改造 FlyWeight不變
package flyweight.composite; public abstract class FlyWeight { /** * 抽象的業務邏輯方法,接受外部狀態作為參數 */ abstract public void operation(String outerState); }ConcreteFlyWeight不變
package flyweight.composite; public class ConcreteFlyWeight extends FlyWeight { private String innerState = null; public ConcreteFlyWeight(String innerState) { this.innerState = innerState; } /** * 外部狀態作為參數傳遞 */ @Override public void operation(String outerState) { System.out.println("innerState = " + innerState + " outerState = " + outerState); } }新增加不共用的子類也就是組合的享元子類 內部使用list 維護單純享元模式對象,提供add方法進行添加 提供operation操作
package flyweight.composite; import java.util.ArrayList; import java.util.List; public class UnsharedConcreateFlyWeight extends FlyWeight { private String innerState = null; public UnsharedConcreateFlyWeight(String innerState) { this.innerState = innerState; } private List<FlyWeight> list = new ArrayList<>(); public void add(FlyWeight flyWeight) { list.add(flyWeight); } @Override public void operation(String outerState) { for (FlyWeight flyWeight:list) { flyWeight.operation(outerState); } } }FlyWeightFactory工廠類進行改造 新增加public UnsharedConcreateFlyWeight getCompositeFylWeight(String state) 用於獲得組合享元對象
package flyweight.composite; import java.util.HashMap; public class FlyWeightFactory { /** * 單例模式 餓漢式創建 */ private static FlyWeightFactory instance = new FlyWeightFactory(); /** * 使用HashMap管理享元池 */ private HashMap<String, Object> hm = new HashMap<>(); /** * 管理複合享元對象 */ private HashMap<String, Object> compositeHm = new HashMap<>(); private FlyWeightFactory() { } /** * 單例全局訪問介面獲取工廠 */ public static FlyWeightFactory getInstance() { return instance; } /** * 根據innerState獲取池中的對象 * 存在返回,不存在創建並返回 */ public FlyWeight getFylWeight(String innerState) { if(hm.containsKey(innerState)){ return (FlyWeight) hm.get(innerState); }else{ FlyWeight flyWeight = new ConcreteFlyWeight(innerState); hm.put(innerState,flyWeight); return flyWeight; } } /** * 根據innerState獲取池中的對象 * 存在返回,不存在創建並返回 */ public UnsharedConcreateFlyWeight getCompositeFylWeight(String state) { if(compositeHm.containsKey(state)){ return (UnsharedConcreateFlyWeight) compositeHm.get(state); }else{ UnsharedConcreateFlyWeight flyWeight = new UnsharedConcreateFlyWeight(state); compositeHm.put(state,flyWeight); return flyWeight; } } }測試類也進行改造
package flyweight.composite; public class Test { public static void main(String[] args){ FlyWeightFactory flyWeightFactory = FlyWeightFactory.getInstance(); FlyWeight flyWeight1 = flyWeightFactory.getFylWeight("First"); FlyWeight flyWeight2 = flyWeightFactory.getFylWeight("Second"); FlyWeight flyWeight3 = flyWeightFactory.getFylWeight("First"); System.out.println(flyWeight1); System.out.println(flyWeight2); System.out.println(flyWeight3); System.out.println("###########################################"); flyWeight1.operation("outer state XXX"); flyWeight2.operation("outer state YYY"); flyWeight3.operation("outer state ZZZ"); System.out.println("###########################################"); UnsharedConcreateFlyWeight compositeFlyWeight = flyWeightFactory.getCompositeFylWeight("composite"); compositeFlyWeight.add(flyWeight1); compositeFlyWeight.add(flyWeight2); compositeFlyWeight.operation("composite out state OOO"); } }
![image_5c087c0b_5108 image_5c087c0b_5108](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093617363-1168855663.png)
![image_5c087c0b_4c7c image_5c087c0b_4c7c](https://img2018.cnblogs.com/blog/897393/201812/897393-20181206093617802-2022395500.png)
使用場景
如果有下列情況,則可以考慮使用享元模式- 應用程式中使用了大量的對象
- 大量的對象明顯增加了程式的存儲運行開銷
- 對象可以提取出內部狀態,並且可以分離外部狀態