[學習筆記]設計模式之Flyweight

爲方便讀者,本文已添加至索引:html

寫在前面

Flyweight(享元)模式運用共享技術,能夠有效地支持大量細粒度的對象。今天咱們會去參觀小霍比特人們的釀酒工坊……等等,不是享元模式嗎?那好吧,咱們推遲到示例一節中前往參觀。設計模式

咱們在作面向對象的設計時,經常但願能用對象來表示某個具體的事物,好比一個紅富士蘋果或是一輛凱迪拉克跑車。當咱們把這種思惟帶到一些程序設計任務中去時,可能就會遭遇處處理存儲開銷和程序自己靈活性的一個平衡問題。例如,咱們在設計一個遊戲,主人公走到一片蘋果園,看見滿滿一屏幕的蘋果。更高級的是,每一個蘋果都能與主人公進行交互,不管是近處的(直接摘下來吃了),仍是遠處的(我拿石頭扔,我扔)。若是每一個蘋果都用不一樣的對象來表示,的確能夠極大提升這個遊戲的視覺效果,由於這樣咱們能夠給每一個蘋果定製徹底不一樣的外觀(這屬於蘋果的內部因素),俗話說,世界上沒有兩個相同的蘋果(...葉子如是,蘋果應該也不差)。可是,若是蘋果園裏,有成千上萬個蘋果呢?這能形成巨大的存儲開銷。事實上,玩遊戲的時候,咱們纔不會太在乎這個蘋果長什麼樣,除非是兩個徹底不一樣種類的蘋果。因此,若是能只存儲一個蘋果的外觀,渲染成千上萬個(存在距離、光照等外部因素),咱們也不會以爲哪裏不舒服,仍是會朝那些蘋果扔石頭。這隻被共享的蘋果,便引出了咱們的Flyweight模式。先來看看它的一些要點。數組

要點梳理

  • 目的分類
    • 對象結構型模式
  • 範圍準則
    • 對象(該模式處理對象間的關係,這些關係在運行時刻是能夠變化的,更具動態性)
  • 主要功能
    • 運用共享技術有效地支持大量細粒度的對象
  • 適用狀況
    • 當如下狀況都成立時,使用Flyweight模式:
    • 一個應用程序使用了大量的對象
    • 徹底因爲使用大量的對象,形成很大的存儲開銷
    • 對象的大多數狀態均可變爲外部狀態
    • 若是刪除對象的外部狀態,那麼能夠用相對較少的共享對象取代不少組對象
    • 應用程序不依賴於對象標識
  • 參與部分
    • Flyweight:描述一個接口,經過這個接口flyweight能夠接受並做用於外部狀態
    • ConcreteFlyweight:實現上述接口,併爲內部狀態(若是有的話)增長存儲空間。ConcreteFlyweight對象必須是可共享的。它所存儲的狀態必須是內部的,它必須獨立於對象的場景
    • UnsharedConcreteFlyweight:並不是全部的Flyweight子類都須要被共享,UnsharedConcreteFlyweight一般將ConcreteFlyweight做爲子節點
    • FlyweightFactory:建立並管理Flyweight對象,而且確保合理地共享Flyweight對象。
    • Client:維持一個對flyweight的引用,計算或存儲一個(多個)flyweight的外部狀態。
  • 協做過程
    • flyweight執行時所需的狀態一定是內部的或外部的。內部狀態存儲於ConcreteFlyweight對象之中;而外部對象則由Client對象存儲或計算。當用戶調用flyweight對象的操做時,將該狀態傳遞給它。
    • 用戶不該直接對ConcreteFlyweight類進行實例化,而只能從FlyweightFactory對象獲得ConcreteFlyweight對象,這能夠保證對它們適當地進行共享。
  • UML圖

示例分析 - 霍比特人的釀酒工坊

還記得這羣可愛的小霍比特人們嗎?(若是不瞭解他們,請見AbstractFactory筆記)喜歡開Party的他們,天然得有喝不完的好酒相伴。時の魔導士早先的時候曾給他們留下過一座釀酒工坊WineFactory。這座工坊能夠生產各式基酒,譬如威士忌Whiskey,朗姆酒Rum,葡萄酒Wine等等。小霍比特人們,能夠根據本身的口味,添加一些果汁、牛奶、咖啡、糖等等輔料,從而調製出一杯美味的雞尾酒Cocktail。學習

在這個魔法世界裏,咱們忽略每種基酒的釀造工藝上的不一樣,而假定它們的內部狀態是相同的(都含有酒精),區分它們的僅僅是口感的類型不一樣。所以,咱們能夠將基酒視做爲一類ConcreteFlyweight,基酒類BaseWine繼承自Drinkspa

 1 class Drink {
 2 public:
 3     virtual ~Drink();    
 4     
 5     virtual void mix(Ingredient&); // can mix with other ingredients.
 6     virtual void drink();
 7  protected:
 8     Drink();
 9 }
10 
11 class BaseWine : public Drink {
12 public:
13     BaseWine(int type);
14     
15     virtual void mix(Ingredient&);
16 private:
17     int _type;
18 }

當小霍比特人拜訪釀酒工坊WineFactory,想要來一杯的時候。工坊會先看看他要的這種基酒是否已經釀造好了,有的話,直接取一份給他;不然,就釀造出取之不盡的這種基酒。設計

 1 #define WHISKEY 1
 2 #define RUM     2
 3 #define VODKA   3
 4 #define GIN     4
 5 // ... other definitions ...
 6 #define MAX_TYPES 10
 7 
 8 class WineFactory {
 9 public:
10     WineFactory();
11     virtual ~WineFactory();
12     virtual BaseWine* createWine(int type);
13     
14 private:
15     BaseWine* _wine[MAX_TYPES];
16 }
17 
18 // Initialize the wine pool.
19 WineFactory::WineFactory() {
20     for (int i = 0; i < MAX_TYPES; ++i) {
21         _wine[i] = 0;
22     }
23 }
24 
25 // Check the pool before create wine.
26 BaseWine* WineFactory::createWine(int type) {
27     if (!_wine[type]) {
28         _wine[type] = new BaseWine(type);
29     }
30     
31     return _wine[type];
32 }

咱們看到,_wine數組包含一些指針,指向以基酒品種爲索引的BaseWine,createWine方法首先會查找這個數組,若是數組中存在這種BaseWine,則能夠直接返回,不然,new一個新的。這樣作的一個好處是,酒的種類每每是有限且很少的,可是咱們能夠卻能夠共享這些種類並用在不少不少場景下。這個簡單例子的UML圖以下:指針

特色總結

Flyweight主要針對存儲節約提出瞭解決方案,它所節約的部分主要由如下幾個因素決定:code

  1. 由於共享,實例總數減小的數目
  2. 對象內部狀態的平均數目
  3. 外部狀態是計算的仍是存儲的

共享的Flyweight越多,存儲節約也就越多。節約量隨着共享狀態的增多而增大。當對象使用大量的內部及外部狀態,而且外部狀態是計算出來的而非存儲的時候,節約量將達到最大。因此,能夠用兩種方法來節約存儲:用共享減小內部狀態的消耗,用計算時間換取對外部狀態的存儲。htm

在使用Flyweight模式時,咱們須要注意如下幾點:對象

  1. 刪除外部狀態。該模式的可用性在很大程度上取決因而否容易識別外部狀態並將它從共享對象中刪除。
  2. 管理共享對象。由於對象是共享的,用戶不能直接對它進行實例化,所以FlyweightFactory能夠幫助用戶查找某個特定的Flyweight對象。

寫在最後

今天的筆記就到這裏了,歡迎你們批評指正!若是以爲能夠的話,好文推薦一下,我會很是感謝的!

相關文章
相關標籤/搜索