享元模式:
以共用的方式高效地支援大量的細粒度對象。
享元對象的狀態:
1:內蘊狀態(Internal State)內蘊狀態儲存在享元對象內部且不會隨環境改變而改變。因此內蘊狀態並可以共用。
2:外蘊狀態(External State)。外蘊狀態是隨環境改變而改變的、不可以共用的狀態。享元對象的外蘊狀態必須由用戶端儲存,並在享元對象被建立之後,在需要使用的時候再傳入到享元對象內部。外蘊狀態與內蘊狀態是相互獨立的。
享元模式的應用條件:
1: 一個系統有大量的對象。
2:這些對象耗費大量的記憶體。
3:這些對象的狀態中的大部分都可以外部化。
4:這些對象可以按照內蘊狀態分成很多的組,當把外蘊對象從對象中剔除時,每一個組都可以僅用一個對象代替。
5:軟體系統不依賴於這些對象的身份,換言之,這些對象可以是不可分辨的。
.NET的享元模式:
.NET中的String類型就是運用了享元模式。.NET中如果第一次建立了一個字串對象s1,下次再建立相同的字串s2時只是把它的引用指向s1所引用的具體對象,這就實現了相同字串在記憶體中的共用。下面的程式來示範s1和s2的引
用是否一致: 輸出的結果為True。
string s1 = "測試字串一";
string s2 = "測試字串一";
Console.WriteLine(Object.ReferenceEquals(s1, s2));
注意:如果再有一個字串s3,它的初始值為“測試字串”,再對它進行操作s3 = s3 + “一”,這時雖然s1和s3的值相同,但是它們的引用是不同的。
享元模式的分類:
1:單純享元模式;
2:複合享元模式。
第一:單純享元模式。
1: 單純享元模式結構圖:
2:單純享元模式構成說明:
1>:抽象享元(Flyweight)角色:此角色是所有的具體享元類的超類,為這些類規定出需要實現的公用介面。那些需要外蘊狀態(External State)的操作可以通過調用商業方法以參數形式傳入。
/// <summary>
/// "Flyweight"
/// </summary>
abstract class Flyweight
{
// Methods
/// <summary>
/// 抽象享元對象的商業方法
/// </summary>
/// <param name="extrinsicstate">外蘊狀態</param>
abstract public void Operation(int extrinsicstate);
}
2>:具體享元(ConcreteFlyweight)角色:實現抽象享元角色所規定的介面。如果有內蘊狀態的話,必須負責為內蘊狀態提供儲存空間。享元對象的內蘊狀態必須與對象所處的周圍環境無關,從而使得享元對象可以在系統內共用的。
/// <summary>
/// "ConcreteFlyweight"
/// </summary>
class ConcreteFlyweight : Flyweight
{
private string intrinsicstate = "A";
// Methods
override public void Operation(int extrinsicstate)
{
Console.WriteLine("ConcreteFlyweight: intrinsicstate {0}, extrinsicstate {1}",
intrinsicstate, extrinsicstate);
}
}
3>:享元工廠(FlyweightFactory)角色:本角色負責建立和管理享元角色。本角色必須保證享元對象可以被系統適當地共用。當一個用戶端對象調用一個享元對象的時候,享元工廠角色會檢查系統中是否已經有一個複合要求的享元對象。如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當建立一個合適的享元對象。
注意:用戶端不可以直接執行個體化享元類,必須通過享元工廠類來建立,因為享元工廠類在系統中只能有一個,所以可以結合單例模式來改善。當用戶端需要單純享元對象時,需要調用享元工廠的Singleton()方法,此時工廠會取得所有的單純享元對象,然後傳入所需的單純享元對象的內蘊狀態,Factory 方法負責產生所需要的享元對象。
/// <summary>
/// "FlyweightFactory"
/// </summary>
class FlyweightFactory
{
// Fields
private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();
private static readonly FlyweightFactory instance = new FlyweightFactory();
/// <summary>
/// Constructors
/// </summary>
private FlyweightFactory()
{
}
// Methods
/// <summary>
/// 從享元工廠中生產出一個具體的享元對象
/// </summary>
/// <param name="key">內蘊狀態</param>
/// <returns></returns>
public Flyweight GetFlyweight(string key)
{
return ((Flyweight)flyweights[key]);
}
/// <summary>
/// 享元工廠單例方法
/// </summary>
/// <returns></returns>
public static FlyweightFactory Singleton()
{
return FlyweightFactory.instance;
}
/// <summary>
/// 向享元工廠對象增加一個享元對象
/// </summary>
/// <param name="sKey">內蘊狀態</param>
/// <param name="_Flyweight">具體享元對象</param>
public void AddFlyweight(string sKey, Flyweight _Flyweight)
{
flyweights.Add(sKey , _Flyweight);
}
public Flyweight factory(string sKey)
{
if (flyweights.ContainsKey(sKey))
{
return this.GetFlyweight(sKey);
}
else
{
this.AddFlyweight(sKey, new ConcreteFlyweight());
return this.GetFlyweight(sKey);
}
}
}
4>:用戶端(Client)角色:需要維護一個對所有享元對象的引用;需要自行儲存所有享元對象外蘊狀態。
// 初始化外蘊狀態值
int extrinsicstate = 22;
//享元工廠對象使用單例
FlyweightFactory f = FlyweightFactory.Singleton () ;
//調用過程
//向享元工廠對象請求一個內蘊狀態為"X"的單純享元對象
Flyweight fx = f.factory("X");
//調用X的商業方法,X的外蘊狀態值為21
fx.Operation(--extrinsicstate);
Flyweight fy = f.factory("Y");
fy.Operation(--extrinsicstate);
Flyweight fz = f.factory("Z");
fz.Operation(--extrinsicstate);
第二:複合享元模式。
複合享元模式結構圖:
結構對象說明:
1:抽象享元角色:此角色是所有的具體享元類的超類,為這些類規定出需要實現的公用介面。那些需要外蘊狀態(External State)的操作可以通過方法的參數傳入。抽象享元的介面使得享元變得可能,但是並不強制子類實行共用,因此並非所有的享元對象都是可以共用的。
2:具體享元(ConcreteFlyweight)角色:實現抽象享元角色所規定的介面。如果有內蘊狀態的話,必須負責為內蘊狀態提供儲存空間。享元對象的內蘊狀態必須與對象所處的周圍環境無關,從而使得享元對象可以在系統內共用。有時候具體享元角色又叫做單純具體享元角色,因為複合享元角色是由單純具體享元角色通過複合而成的。
3:複合享元(UnsharableFlyweight)角色:複合享元角色所代表的對象是不可以共用的,但是一個複合享元對象可以分解成為多個本身是單純享元對象的組合。複合享元角色又稱做不可共用的享元對象。
4:享元工廠(FlyweightFactoiy)角色:本角色負責建立和管理享元角色。本角色必須保證享元對象可以被系統適當地共用。當一個用戶端對象請求一個享元對象的時候,享元工廠角色需要檢查系統中是否已經有一個符合要求的享元對象,如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當建立一個新的合適的享元對象。
5:用戶端(Client)角色:本角色還需要自行儲存所有享元對象的外蘊狀態。
享元模式的優點:
大幅度地降低記憶體中對象的數量。
享元模式的缺點:
1:享元模式使得系統更加複雜。為了使對象可以共用,需要將一些狀態外部化,這使得程式的邏輯複雜化。
2:享元模式將享元對象的狀態外部化,而讀取外部狀態使得已耗用時間稍微變長。
總結:
享元模式一般是解決系統效能問題的,所以經常用於底層開發,在項目開發中並不常用。
註:
本文參考:《Java與模式》
http://www.cnblogs.com/zhenyulu/articles/55793.html