天天看點

享元模式(學習筆記)

  運用共享技術有效的支援大量細粒度的對象

   假設開發了一款簡單的遊戲:玩家們在地圖上移動并進行互相射擊。大量的子彈、飛彈和爆炸彈片在整個地圖上穿行,為玩家提供緊張刺激的遊戲體驗。但是,運作了幾分鐘後,遊戲因為記憶體容量不足而發生了崩潰。研究發現,每個粒子(一顆子彈、 一枚飛彈或一塊彈片)都由包含完整資料的獨立對象來表示。當玩家在遊戲中鏖戰進入高潮後的某一時刻,遊戲将無法在剩餘記憶體中載入建立粒子,于是程式就崩潰了

  

享元模式(學習筆記)

  仔細觀察粒子Particle類,會注意到顔色(color)和精靈圖(sprite)這兩個成員變量所消耗的記憶體要比其他變量多得多。另外,對所有例子來說,這兩個成員變量所存儲的資料幾乎完全一樣 (比如所有子彈的顔色和精靈圖都一樣)。每個粒子的另一些狀态 (坐标、 移動矢量和速度)則是不同的,因為這些成員變量的數值會不斷變化。内在狀态存儲于flyweight中,它包含了獨立于場景的資訊,這些資訊使得flyweight可以被共享。而外部狀态取決于flyweight場景,并根據場景而變化,是以不可共享。使用者對象負責在必要的時候将外部狀态傳遞給Flyweight

享元模式(學習筆記)

  

程式需要生成巨大的相似對象,以至于消耗目标對象的所有記憶體

對象中包含可抽取且能在多個對象間共享的重複狀态

享元模式(學習筆記)

  使用FlyWeight模式時,傳輸、查找和/或計算外部狀态都會産生運作時開銷,尤其當flyweight原先被存儲為内部狀态時。然而,空間上的節省抵消了這些開銷。共享的flyweight越多,空間節省越大

  存儲節約由以下因素決定:

  1. 由于共享帶來的執行個體總數減少的數量

  2. 對象内部狀态的平均數量

  3. 外部狀态是計算的還是存儲的

  共享的flyweight越多,存儲節約的也就越多。節約量随着共享狀态的增多而增大。當對象使用大量的内部及外部狀态,并且外部狀态是計算出來的而非存儲的時候,節約量将達到最大

  如果渲染一片森林 (1,000,000 棵樹)!每棵樹都由包含一些狀态的對象來表示 (坐标和紋理等)。 盡管程式能夠完成其主要工作,但很顯然它需要消耗大量記憶體。因為,太多樹對象包含重複資料 (名稱、 紋理和顔色)。是以我們可用享元模式來将這些數值存儲在單獨的享元對象中 (Tree­Type類)。現在我們不再将相同資料存儲在數千個 Tree對象中,而是使用一組特殊的數值來引用其中一個享元對象。用戶端代碼不會知道任何事情, 因為重用享元對象的複雜機制隐藏在了享元工廠中

  trees/Tree.java: 包含每棵樹的獨特狀态

  trees/TreeType.java: 包含多棵樹共享的狀态

  trees/TreeFactory.java: 封裝建立享元的複雜機制

  forest/Forest.java: 我們繪制的森林

  Demo.java: 用戶端代碼

  運作結果

享元模式(學習筆記)

可以使用享元模式實作組合模式樹的共享葉節點以節省記憶體

享元展示了如何生成大量的小型對象,外觀模式則展示了如何用一個對象來代表整個子系統

如果你能将對象的所有共享狀态簡化為一個享元對象,那麼享元就和單例模式類似了。但這兩個模式有兩個根本性的不同: 

  1. 隻會有一個單例實體,但是享元類可以有多個實體,各實體的内在狀态也可以不同

  2. 單例對象可以是可變的。享元對象是不可變的 

  java.lang.Integer#valueOf(int) (以及 Boolean、Byte、Character、Short、Long 和 Big­Decimal)

  識别方法:享元可以通過建構方法來識别,它會傳回緩存對象而不是建立新的對象