基於面向對象思想設計的應用程序有時遇到須要場景大量相同或顯示對象實例的場景,這些數量龐大的實例極可能會消耗不少系統資源,最直接的就是內存了。好比要一款圍棋遊戲,若是每次落子都新建一個對象,將會佔用大量內存,而實際上棋子只有黑白兩色,不一樣的只是落子位置而已。另外,大量的主動型對象還會佔用不少CPU和顯卡的計算資源,舉個極端的例子,某個遊戲的沙漠場景,爲了使遊戲具備豐富的視覺效果,要求每一粒沙子都要隨着光線而有不一樣的呈現效果不一樣,這時候直接new固然也是不現實的。設計模式
享元模式
享元模式提供了一種針對這類場景的解決方案。它經過共享已經存在的對象來大幅度減小須要建立的對象數量、避免大量類似對象的開銷,從而提升系統資源的利用率,支持大量細粒度對象的複用。緩存
UML類圖:
ide
代碼實現this
public abstract class Flyweight { //內部狀態 public string Instrinsic { get; set; } //外部狀態 protected string Extrinsic { get; set; } public Flyweight(string extrinsic) { this.Extrinsic = extrinsic; } //定義業務操做 public abstract void Operate(int id); } public class ConcreteFlyweight : Flyweight { //接受外部狀態 public ConcreteFlyweight(String extrinsic) : base(extrinsic) { } //根據外部狀態進行邏輯處理 public override void Operate(int id) { Console.WriteLine("Flyweight:" + id); } } public class UnsharedConcreteFlyweight : Flyweight { public UnsharedConcreteFlyweight(String extrinsic) : base(extrinsic) { } public override void Operate(int id) { Console.WriteLine("不共享的Flyweight:" + id); } } public class FlyweightFactory { //定義一個池容器 private static Dictionary<String, Flyweight> pool = new Dictionary<String, Flyweight>(); //享元工廠 public static Flyweight GetFlyweight(string extrinsic) { Flyweight flyweight = null; if (pool.ContainsKey(extrinsic)) { flyweight = pool[extrinsic]; Console.Write($"已有{extrinsic} "); } else { flyweight = new ConcreteFlyweight(extrinsic); pool.Add(extrinsic, flyweight); Console.Write($"新建{extrinsic} "); } return flyweight; } }
享元模式的核心是用一個池容器來緩存須要共享的對象,C#能夠用Dictionary來實現。spa
內部狀態與外部狀態
因爲這些數量較大的細粒度對象有着相近的性質,爲了能共享這些對象,須要將這些對象的信息分爲兩個部分:內部狀態和外部狀態。
設計
- 內部狀態指對象共享出來的信息,存儲在享元對象內部而且不會隨環境的改變而改變;
- 外部狀態指會隨環境改變而改變的、不可共享的狀態。
好比前面圍棋的例子中,棋子的黑白兩色就可做爲內部狀態,落子位置則是外部狀態,將內部狀態(黑、白兩色)做爲對象間的本質區別,只須要兩個對象就能夠了,而後配合外部狀態(落子位置)的變化,就能夠表示所有的棋子。
圍棋例子的誤導
使用圍棋這個例子會容易讓人產生一個困惑:既然實際上只有兩個對象(黑、白),那麼是如何讓同一個對象即出如今位置A,又出如今位置B的呢?難不成像薛定諤的貓那樣,能夠有多種狀態?
實際上這裏所謂的共享對象,共享的應該是對象的行爲。在圍棋遊戲中能夠理解爲,在畫布上繪製的棋子圖案。每一個棋子對象都有個繪製棋子圖案的行爲,經過設置不一樣的外部狀態(落子位置),就棋子的繪圖行爲就會在畫布上不一樣的位置繪製圖案。
code
享元模式的適用場景
- 系統中有大量對象。
- 這些對象消耗大量內存。
- 這些對象的狀態大部分能夠外部化。
- 這些對象能夠按照內部狀態分爲不少組,當把外部對象從對象中剔除出來時,每一組對象均可以用一個對象來代替。
- 系統不依賴於這些對象的身份,這些對象是不可分辨的。
享元模式的優缺點
優勢對象
- 大大減小對象的建立,下降系統的資源佔用,提升效率。
- 因爲抽離出了外部狀態和內部狀態,外部狀態相對獨立,不會影響到內部狀態,因此享元模式使得享元對象可以在不一樣的環境被共享。
缺點 - 增長了系統的複雜度,須要分離出外部狀態和內部狀態,並且外部狀態具備固有化的性質,不該該隨着內部狀態的變化而變化,不然會形成系統的混亂。
- 爲了使對象能夠共享,享元模式須要將享元對象的狀態外部化,而讀取外部狀態使得運行時間變長。
參考書籍:
王翔著 《設計模式——基於C#的工程化實現及擴展》
blog