基本介紹:
享元模式的定義:運用共享技術有效地支援大量細粒度的物件重複使用。適用於大量小粒度的物件造成的執行效率和記憶體使用效率低下的情況。
“享元”顧名思義,“享”共享的意思,“元”單元,最小物件,零部件的意思。
即從字面意思不難看出,該模式旨在共享一些零部件供其使用。
想要實現對這些零部件的重複使用,那必然需要一個類來統籌安排,負責它們的建立、使用和維護。
如果想要實現一個零部件的使用,可以使用單例模式,所以享元模式其實也可以看做是單例模式的複數模式。
它們都是在一個固定類中對物件進行建立和維護。
舉例說明:
比如五子棋遊戲,構成遊戲的元件,無非就是無數個黑棋和白棋。
黑棋和白棋就可以看做是兩個最小的單元,在對戰過程中就是在重複的建立這些單元。
如果是一般模式一盤棋局必然要建立N多個黑棋和白棋的物件,如果是一盤遊戲還可以湊合使用。
大家想象一下,如果是個遊戲平臺,可以同時開展1000盤這樣的棋局,那必然需要建立N*1000個黑白棋子物件。
其實這些物件都是重複的,只有很少一部分屬性(狀態)不同而已,相同的是棋子本身,不同的是棋子的顏色,比如黑棋和白棋之分。
另外該棋子在哪個棋盤、在哪個棋盤座標就屬於不可被共享的部分了,這部分內容就是非共享的。
既然弄清楚了相同的部分和不同部分,我們就可以把相同的部分進行共享,物件個數從原來的N*1000個物件降到了2個物件。
基本結構:
透過例子也不難看出,享元模式建立的物件存在兩個狀態:
內部狀態:可以被共享的狀態。儲存在享元資訊內部,並且不會隨環境的改變而改變。在這裡指的是棋子本身,它們不會隨著棋局和選手的變化而變化。
外部狀態:不可被共享的狀態。隨環境的改變而改變。在這裡指的是黑白棋子顏色之分。
這裡容易讓人產生誤區,容易把棋子所處座標和棋盤等歸在外部狀態,這就大錯特錯了。
能共享的都是最基本的單元,而棋子的座標和棋盤是不斷變化的,隨著棋局的不同而不同,這部分是不可以被共享的,也不能被共享。
享元模式的主要有以下角色:
抽象享元角色(Flyweight):通常是一個介面或抽象類,宣告瞭具體享元類公共的方法,這些方法可以為外界提供內部狀態和設定外部狀態。
具體享元角色(Concrete Flyweight):實現了抽象享元類,在該類中為內部狀態提供了儲存空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元物件。
非享元角色 (Unsharable Flyweight):並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為非共享具體享元類;當需要一個非共享具體享元類的物件時可以直接透過例項化建立。本例項中,棋盤類就是非享元部分。
享元工廠角色(Flyweight Factory):負責建立和管理享元角色。當客戶物件請求一個享元物件時,享元工廠檢査系統中是否存在符合要求的享元物件,如果存在則提供給客戶;如果不存在的話,則建立一個新的享元物件。
優缺點:
優點:降低了系統中物件的數量,從而降低了系統中細粒度物件給記憶體帶來的壓力。享元模式中的外部狀態相對獨立,且不影響內部狀態。
缺點:為了使物件可以共享,需要將享元物件的部分狀態外部化,分離內部狀態和外部狀態,這使得程式的邏輯更復雜,使系統複雜化。
具體例項:
-
抽象享元角色
1 /// <summary> 2 /// 享元抽象類 3 /// </summary> 4 public abstract class ChessPieces 5 { 6 public string Colour; 7 public ChessPieces(string strColour) 8 { 9 Colour = strColour; 10 } 11 12 13 /// <summary> 14 /// 落子規則 15 /// </summary> 16 /// <param name="chessBoard">棋盤資訊</param> 17 public void MoveInChess(ChessBoard chessBoard) 18 { 19 //這裡可以將棋盤資訊透過引數方式注入 20 } 21 22 //還可以設定一些 棋子的規則等等 23 }
享元抽象類主要是規範具體享元類供其繼承,並提供其共有的屬性和方法。
本例項只是最簡單的使用建構函式引數注入方式,將棋子外部狀態(顏色)更新。
而內部狀態,比如棋子的具體下棋規則等等,可以在此類中宣告。
比如此例項MoveInChess方法就是透過引數注入的形式將棋盤資訊注入到內容,從而落實落子的具體規則。
規則是通用的,可以共享的,所以可以是內部狀態的一部分。
- 具體享元角色
1 /// <summary> 2 /// 白棋 3 /// </summary> 4 public class BlackPieces : ChessPieces 5 { 6 public BlackPieces(string strColour) : base(strColour) 7 { 8 9 } 10 } 11 12 /// <summary> 13 /// 黑棋 14 /// </summary> 15 public class WhitePieces : ChessPieces 16 { 17 public WhitePieces(string strColour) : base(strColour) 18 { 19 20 } 21 }
繼承自抽象享元類,具體實現其方法。
此例項只是透過建構函式簡單的將棋子分成黑白棋子,實際中會有各種屬性或方法需要在此類中實現。
- 享元工廠角色
/// <summary> /// 棋子工廠 /// </summary> public class WuziqiFactory { // 單例模式工廠 private static WuziqiFactory wuziqiFactory; // 快取存放共享物件 private static Dictionary<string, ChessPieces> keyValuePairs = new Dictionary<string, ChessPieces>(); // 私有化構造方法 private WuziqiFactory() { if (keyValuePairs.Count == 0) { keyValuePairs.Add("Black", new BlackPieces("Black")); keyValuePairs.Add("White", new WhitePieces("White")); } } // 獲得單例工廠 public static WuziqiFactory GetInstance { get { if (wuziqiFactory == null) { wuziqiFactory = new WuziqiFactory(); } return wuziqiFactory; } } // 獲取棋子 public ChessPieces GetChessPieces(String type) { if (keyValuePairs.ContainsKey(type)) { return keyValuePairs[type]; } return null; } }
此例項使用單例模式建立工廠物件,保證整個專案生命週期內只存在一個棋子工廠物件,並使用GetInstance進行返回具體物件。這裡可以加上鎖防止併發等問題。
另外工廠的建構函式對可共享物件進行了快取,使用GetChessPieces獲取棋子物件時,可保證其不重複建立。保證整個專案生命週期內只存在黑白兩個棋子物件。
- 非享元角色
1 /// <summary> 2 /// 棋盤 3 /// </summary> 4 public class ChessBoard 5 { 6 //棋盤編號 7 public int ChessBoardId { get; set; } 8 9 10 //黑方棋手 11 //白方棋手 12 //棋盤棋子佈局等等屬性 13 14 /// <summary> 15 /// 初始化棋盤 16 /// </summary> 17 public ChessBoard() 18 { 19 //可以透過建構函式 初始化棋盤基礎屬性 20 } 21 }
非享元部分,也就是不能共享的部分。
棋盤的編號、棋盤對局雙方資訊和棋盤落子情況等都是每個棋盤獨有的,不可共享。
至於棋盤的落子和整體維護可以透過引數注入等形式交給棋子共享物件進行研判和操作,或者直接在棋盤類中進行宣告都可以,這就看具體制定的規則了。
-
客戶端
1 /// <summary> 2 /// 客戶端 3 /// </summary> 4 class Client 5 { 6 static void Main(string[] args) 7 { 8 //建立棋盤 透過建構函式或者函式引數等形式初始化棋手、棋盤資訊 9 ChessBoard chessBoard = new ChessBoard(); 10 //獲取黑方棋子1 11 ChessPieces blackPieces1 = WuziqiFactory.GetInstance.GetChessPieces("Black"); 12 Console.WriteLine("棋子:" + blackPieces1.Colour); 13 //獲取白方棋子1 14 ChessPieces whitePieces1 = WuziqiFactory.GetInstance.GetChessPieces("White"); 15 Console.WriteLine("棋子:" + whitePieces1.Colour); 16 17 //判斷兩個棋子是否是同一個物件 18 Console.WriteLine("判斷兩個不同顏色的棋子是否是同一個物件" + blackPieces1.Equals(whitePieces1) + "\r\n"); 19 20 //獲取黑方棋子2 21 ChessPieces blackPieces2 = WuziqiFactory.GetInstance.GetChessPieces("Black"); 22 Console.WriteLine("棋子:" + blackPieces2.Colour); 23 //獲取白方棋子2 24 ChessPieces whitePieces2 = WuziqiFactory.GetInstance.GetChessPieces("White"); 25 Console.WriteLine("棋子:" + whitePieces2.Colour); 26 27 //判斷同一個顏色的兩個棋子是否是同一個物件 28 Console.WriteLine("判斷兩個不同顏色的棋子是否是同一個物件" + blackPieces1.Equals(blackPieces2) + "\r\n"); 29 30 Console.ReadKey(); 31 } 32 }
總結:
實現享元工廠類時使用單例模式和簡單工廠模式,確保物件的唯一性,並提供方法向客戶端返回享元物件。