文章目錄
- Gof定義
- 動機
- Flyweight模式的幾個要點
Gof定義
運用共用技術有效地支援大量細粒度的對象。
動機
採用純粹對象方案的問題在於大量細粒度的對象會很快充斥在系統中,從而帶來很高的運行時代價——主要指記憶體需求方面的代價。如何在避免大量細粒度對象問題的同時,讓外部客戶程式仍然能夠透明地使用物件導向的方式來進行操作?這需要用到享元模式,不過應用享元模式是需要進行評估的,也就是說在當前情況下是否會對系統造成效能上的影響,如果會那麼就是用,下面先來看個小例子是如何進行評估的。
假設有一個字元的類Charator,如下:
public class Charator{ char c; Font f;}
為了方便評估,Charator類中的Font類型採用自訂的類型:
public class Font{ string fontName; int size; Color color;}
在客戶代碼中使用Charator類
public class App{ static void Main() { List<Charator> list=new List<Charator>(100000); for (int i = 0; i < list.Count; i++) { Charator charator = new Charator(); list.Add(charator); } }}
在客戶代碼中將Charator類執行個體化了100000次,Charator類中有兩個成員,並且有個成員為Font類型,Font類型中又定義了三個成員,下面將在代碼注釋中標出要佔用的記憶體量:
/// <summary>/// 如果有n個Font對象佔用大小為12bytes*n 其實字串類型/// 除了本身佔用的4bytes外 還有一個引用指標要佔用4bytes,/// 不過這個 不會構成倍乘效應,也就是當有n個對象時不會/// 以n的倍數增長,所以可以忽略 /// </summary>public class Font{ string fontName; //4bytes int size; //4bytes Color color; //4bytes}/// <summary>/// 總共佔用2+20+4+8+2=36bytes/// 第三個的4為指向font的指標/// 第四個的8為虛表指標和記憶體回收和同步/// 最後的2為char的填充位/// </summary>public class Charator{ /// <summary> /// 佔用2bytes /// </summary> char c; /// <summary> /// 總佔用12bytes+4bytes+4bytes =20bytes /// 其中12bytes是三個成員的,另外的兩個4bytes分別為 /// 虛表指標和net記憶體回收機制所需要的一些位,因為每個 /// 類型最終都可以追溯到object類型,所以預設都會有虛函數 /// 4byte的虛表指標式不可少的 /// </summary> Font f; }/// <summary>/// 36bytes * 100000=3600000bytes =3600k=3.6mb/// </summary>public class App{ static void Main() { List<Charator> list=new List<Charator>(100000); for (int i = 0; i < list.Count; i++) { Charator charator = new Charator(); list.Add(charator); } }}
上面的代碼執行會帶來3mb多的記憶體資料,這個對於現在的機器來說算不了什麼,上面例子中是做了100000次的計算,那如果再加兩個數量級到10000000又會怎麼樣了,那就會有300多mb,這個數字肯定是不能接受的,那麼這個時候就要考慮用享元模式了。 看下面改進後的代碼:
public class Font{ string _fontName; int _size; Color _color; public Font(string name, int size, Color color) { _fontName = name; _size = size; _color = color; }}public class Charator{ private static Hashtable fontTable=new Hashtable(); public char C { get; set; } public Font CFont { get; set; } public Font GetFont(string name) { if (!fontTable.ContainsKey(name)) { fontTable.Add(name, new Font(name, 8, Color.Red)); } return (Font)fontTable[name]; }}public class App{ static void Main() { List<Charator> list = new List<Charator>(100000); for (int i = 0; i < list.Count; i++) { Charator charator = new Charator(); charator.C = 'a'; charator.CFont = charator.GetFont("宋體"); list.Add(charator); } }}
上面的代碼主要是在GetFont方法中進行了判斷,如果對象不存在才建立新的執行個體,否則直接返回儲存在HashTable中的對象。下面來看下享元模式的結構圖:
針對上面結構的完整代碼:
/// <summary>/// 享元的抽象類別/// </summary>public abstract class Flyweight{ public abstract void Operation(int extrinsicState);}/// <summary>/// 需要共用的具體類/// </summary>public class ConceteFlyweight : Flyweight{ public override void Operation(int extrinsicState) { Console.WriteLine("需要共用的具體Flyweight類:" + extrinsicState); }}/// <summary>/// 不需要共用的具體類/// </summary>public class UnsharedConcreteFlyeight : Flyweight{ public override void Operation(int extrinsicState) { Console.WriteLine("不需要共用的具體Flyweight類:" + extrinsicState); }}/// <summary>/// 一個工廠類,用來合理建立對象/// </summary>public class FlyweightFactory{ private Dictionary<string, Flyweight> dic =
new Dictionary<string, Flyweight>(); public Flyweight GetFlyweight(string key,bool type) { if (!dic.ContainsKey(key)) { Flyweight flyweight = new UnsharedConcreteFlyeight(); if (type) flyweight = new ConceteFlyweight(); dic.Add(key, flyweight); } return (Flyweight)dic[key]; }}/// <summary>/// 用戶端調用/// </summary>public class App{ static void Main() { int extrinsicState = 26; FlyweightFactory factory = new FlyweightFactory(); Flyweight f1 = factory.GetFlyweight("oec2003", true); f1.Operation(++extrinsicState); Flyweight f2 = factory.GetFlyweight("oec2003", true); f2.Operation(++extrinsicState); Flyweight f3 = factory.GetFlyweight("oec2004", false); f3.Operation(++extrinsicState); }}Flyweight模式的幾個要點
物件導向很好地解決了抽象性的問題,但是作為一個運行在機器中的程式實體,我們需要考慮對象的代價問題。Flyweight設計模式主要解決物件導向的代價問題,一般不觸及物件導向的抽象性問題。
Flyweight採用對象共用的做法來降低系統中對象的個數,從而降低細粒度對象給系統帶來的記憶體壓力。在具體實現方面,要注意對象狀態的處理。
對象的數量太大從而導致對象記憶體開銷加大——什麼樣的數量才算大---這需要我們仔細的根據具體應用情況進行評估,而不能憑空臆斷。
返回開篇(索引)