18 Flyweight享元(結構型)
-
- 享元:
- 享元指:次最輕量級的拳擊選手
- 享元模式以共享的方式高效地支持大量的細粒度對象
- 通過儘量共享實例來避免new出實例。(new不僅消耗內存,還會花費時間)
- 動機:
- 一些應用程序可以從在整個設計過程中使用對象中獲益,但是一個簡單的實現將會非常昂貴。
- 面向對象的文檔編輯器通常使用對象來表示嵌入的元素,如表和圖。
- 這種設計的缺點是成本高。即使是中等大小的文檔也可能需要幾十萬個字符對象,這將消耗大量內存,並可能導致不可接受的運行時開銷。
- 從邏輯上講,文檔中每出現一個給定字符就有一個對象:
- flyweight是一個可以同時在多個上下文中使用的共享對象。
- 某一特定字符對象的每一次出現都引用flyweight對象共享池中的相同實例:
- 什麼時候用:
- 應用程序使用大量對象。
- 存儲成本很高,因爲數量(絕對數量)的對象,例如,漢字。
- 大多數對象狀態可以變成extrinsic外部。
- 一旦外部狀態被移除,許多組對象可能會被相對較少的共享對象所取代。
- 應用程序不依賴於對象標識。
- 結構:
- 享元:
-
- 參與者:
- Flyweight:聲明一個接口,通過該接口,flyweights可以接收和作用於外部的狀態。
- 參與者:
-
-
- ConcreteFlyweight:
- 實現Flyweight接口,併爲內部狀態(如果有的話)添加存儲。
- 一個具體的flyweight對象必須是可共享的。
- 它存儲的任何狀態都必須是固有的;也就是說,它必須獨立於ConcreteFlyweight對象的上下文
- UnsharedConcreteFlyweight
- 並非所有Flyweight子類都需要共享。
- Flyweight接口支持共享;它沒有強制執行。
- FlyweightFactory:
- 創建和管理flyweight對象,並確保正確共享flyweight。
- 當客戶端請求flyweight時,FlyweightFactory對象提供一個現有實例,或者創建一個實例(如果不存在的話)。
- Client:
- 維護對flyweight的引用。
- 計算或存儲flyweight的外部狀態。
- ConcreteFlyweight:
-
-
- 協作:
- 一個flyweight需要功能的狀態必須被描述爲內在的或外在的intrinsic or extrinsic。
- 內部狀態存儲在ConcreteFlyweight對象中;
- 外部狀態由客戶端對象存儲或計算。
- 一個flyweight需要功能的狀態必須被描述爲內在的或外在的intrinsic or extrinsic。
- 協作:
客戶端在調用flyweight的操作時將此狀態傳遞給該flyweight。
-
- 客戶端不應該實例化
-
- 後果:
- Flyweights可能引入與傳輸、查找和/或計算外部狀態相關的運行時成本。
- 存儲節省是幾個因素的函數:
- 實例總數的減少來自於共享
- 每個對象的內部狀態的數量
- 無論外部狀態是計算還是存儲
- 共享的flyweights越多,存儲節省就越大。
- 最大的節省發生在外部狀態可以計算而不是存儲的時候。然後您可以通過兩種方式節省存儲:共享可以降低內部狀態的成本,並且可以用外部狀態交換計算時間。
- 實現問題1:移除外部狀態
- 模式的適用性在很大程度上取決於識別外部狀態並將其從共享對象中移除的容易程度。
- 去除外部狀態無助於降低存儲成本。
- 理想情況下,外部狀態可以從一個單獨的對象結構中計算出來,這個對象結構的存儲需求要小得多。
- (最好)客戶端通過參數將外部狀態存儲並傳遞給flyweight。
- (更好)將外部狀態和相關行爲(代碼)從flyweight一起刪除到客戶端。
- 封裝外部狀態和相應的行爲(代碼)來構建新的類。
- 實現問題2:處理共享對象
- 因爲對象是共享的,所以客戶端不應該直接實例化它們。FlyweightFactory允許客戶定位特定的flyweight。
- FlyweightFactory對象通常使用關聯存儲來讓客戶端查找感興趣的flyweights。管理器根據代碼返回適當的flyweight,如果flyweight還不存在,就創建它。
- 可共享性還意味着某種形式的引用計數或垃圾收集,以便在不再需要flyweight存儲時回收它。然而,如果享元的數量固定且較小,則這兩種方法都不是必要的。
- 後果: