C#設計模式-享元模式
前言
最近開始花點心思研究下設計模式,主要還是讓自己寫的代碼可重用性高、保證代碼可靠性。所謂設計模式,我找了下定義:是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。
為什麼要提倡“Design Pattern(設計模式)”?
根本原因是為了代碼復用,增加可維護性。因此這次我們來學習下設計模式,最後會通過C#語言來實現這些設計模式作為例子,深刻理解其中的精髓。
定義
享元模式:它使用共享物件,用來盡可能減少內存使用量以及分享資訊給盡可能多的相似物件;它適合用於只是因重復而導致使用無法令人接受的大量內存的大量物件。通常物件中的部分狀態是可以分享。常見做法是把它們放在外部數據結構,當需要使用時再將它們傳遞給享元。
之前講到的單例模式,一個類只有一個唯一的對象,也就是說,不管new多少次,只需要創建這個類的一個對象,如果不采用單例模式,每new一次,就會創建一個對象,便會產生大量重復的對象,而這些對象要消耗很大的資源的時候,是會產生資源互搶的延遲的。這個時候享元模式就能幫忙解決這類的問題。
特點
享元模式的意圖是通過共享有效支持大量細粒度的對象,來提供應用程序的性能,節省系統中重復創建對象實例的性能消耗,這個怎麼理解呢?其實就是以下幾點的含義:
1、當我們系統中某個對象類型的實例較多的情況。
2、並且要求這些實例進行分類後,發現真正有區別的分類很少的情況。
例如我們的生活中很多的場景,我們在使用拼音輸入的法的時候,如果說我們每個字都是new一個對象實例的操作的話,那麼內存中的實例就太可怕,這個時候,我們是不是可以考慮將這些重復的字體在內存中只是創建一次,而是通過復用對象的形式,來組織一些可能有多個字符重復的內容呢?
優缺點
優點:
一、
降低了系統中對象的數量,從而降低了系統中細粒度對象給內存帶來的壓力。
缺點:
一、
為了使對象可以共享,需要將一些狀態外部化,這使得程序的邏輯更復雜,使系統復雜化。
二、
享元模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。
實現思路
Pic86
享元模式就是把部分和整體的關系用樹形結構來表示,從而使客戶端能夠把一個個的部分對象和由它們組合起來的整體對象采用同樣的方式來對待。享元模式主要由3部分組成:享元類,具體的享元類,享元工廠類。
復制代碼
#region 客戶端調用
/// <summary>
/// 客戶端調用
/// </summary>
class Client
{
static void Main(string[] args)
{
// 初始化享元工廠
FlyweightFactory factory = new FlyweightFactory();
// 判斷是否已經創建了對象1,如果已經創建就直接使用創建的對象
Flyweight fa = factory.GetFlyweight(0);
if (fa != null)
{
// 把外部狀態作為享元對象的方法調用參數
fa.Operation();
}
// 判斷是否已經創建了字母B
Flyweight fb = factory.GetFlyweight(1);
if (fb != null)
{
fb.Operation();
}
// 判斷是否已經創建了字母C
Flyweight fc = factory.GetFlyweight(2);
if (fc != null)
{
fc.Operation();
}
// 判斷是否已經創建了字母D
Flyweight fd = factory.GetFlyweight(3);
if (fd != null)
{
fd.Operation();
}
else
{
Console.WriteLine("駐留池中不存在對象4");
// 這時候就需要創建一個對象並放入駐留池中
ConcreteFlyweight d = new ConcreteFlyweight("第四個對象");
factory.flyweights.Add(d);
}
Console.Read();
}
}
#endregion
#region 享元工廠,負責創建和管理享元對象
/// <summary>
/// 享元工廠,負責創建和管理享元對象
/// </summary>
public class FlyweightFactory
{
public List<Flyweight> flyweights = new List<Flyweight>();
public FlyweightFactory()
{
flyweights.Add(new ConcreteFlyweight("第一個對象"));
flyweights.Add(new ConcreteFlyweight("第二個對象"));
flyweights.Add(new ConcreteFlyweight("第三個對象"));
}
public Flyweight GetFlyweight(int key)
{
Flyweight flyweight;
if (key >= flyweights.Count)
{
Console.WriteLine("駐留池中不存在對象" + (key + 1));
flyweight = new ConcreteFlyweight("第" + (key + 1) + "個對象");
}
else
{
flyweight = flyweights[key];
}
return flyweight;
}
}
#endregion
#region 抽象享元類,提供具體享元類具有的方法
/// <summary>
/// 抽象享元類,提供具體享元類具有的方法
/// </summary>
public abstract class Flyweight
{
public abstract void Operation();
}
#endregion
#region 具體的享元對象
/// <summary>
/// 具體的享元對象
/// </summary>
public class ConcreteFlyweight : Flyweight
{
private string intrinsicstate;
// 構造函數
public ConcreteFlyweight(string innerState)
{
this.intrinsicstate = innerState;
}
/// <summary>
/// 享元類的實例方法
/// </summary>
/// <param name="extrinsicstate">外部狀態</param>
public override void Operation()
{
Console.WriteLine("調用了具體的對象: {0}", intrinsicstate);
}
}
#endregion
復制代碼
采用享元模式實現的簡單數據庫連接池
DbConnectionPool 類有兩個重要的方法; getConnection(), 從對象池中獲取一個對象, returnConnection(), 把對象還給對象池。我們以兩個哈希表實現對象池,一個稱為freeConnections, 另一個稱為busyConnections. busyConnections 哈希表包含所有正在使用的對象而freeConnections哈希表包含了所有未被使用且可隨時使用的對象。
復制代碼
public interface DbConnectionPool
{
//設定連接池中存放連接的數目
public void SetMaxConns(int numConnections);
//設定打開或者關閉連接池
public void SetConnWitch(string Switch);
//產生連接池
public void initConnPool();
//從連接池獲取連接
public Connection getConnection();
//將連接返回給連接池
public void returnConnection();
//銷毀連接池
public void destroyConnPool();
}
復制代碼
復制代碼
public class GdDbConnectionPool:DbConnectionPool
{
private static string connectionString =
@"server=(local);Trusted Connection=yes;database=myDB";
//默認連接池的大小
static const int defaultMaxConnections = 10;
//存放目前空閒的連接,空閒池
private List<Connnection> freeConnections;
//存放目前正在使用的連接
private List<Connnection> busyConnections;
//設定連接池的大小
private int maxConnections;
//構造函數
public GdDbConnectionPool(int numConnections)
{
maxConnections = numConnections;
freeConnections = null;
busyConnections = null;
}
#region 實現獲取數據庫連接的操作
/// <summary>
/// 實現獲取數據庫連接的操作
/// </summary>
/// <returns></returns>
public Connection getConnection()
{
if (freeConnections == null)
{
return "連接池還沒有創建";
}
object o = null;
lock (this)
{
try
{
foreach (DictionaryEntry myEntry in freeConnections)
{
o = myEntry.Key;
unlocked.Remove(o);
if (Validate(o))
{
locked.Add(o, now);
return o;
}
else
{
Expire(o);
o = null;
}
}
}
}
//獲取空閒池連接
Connection conn = (Connection)freeConnections.get(0);
freeConnections.remove(o);
busyConnections.add(o);
return o;
}
#endregion
#region 產生連接池
/// <summary>
/// 產生連接池
/// </summary>
public void initConnPool()
{
freeConnections = new freeConnections(maxConnections);
busyConnections = new busyConnections(maxConnections);
for (int i = 0; i < maxConnections; i++)
{
freeConnections.add();
}
}
#endregion
#region 從繁忙池中銷毀已經返回的連接
/// <summary>
/// 從繁忙池中銷毀已經返回的連接
/// </summary>
public void returnConnection()
{
busyConnections conn = (Connection)busyConnections.get();
if (conn = null)
throw new Exception("沒有發現繁忙池中有連接");
busyConnections.remove();
freeConnections.add(conn);
}
#endregion
}
復制代碼
在企業級計算的多線程世界中同步是一個極其重要的概念。它被廣泛用於數據庫,消息隊列以及Web 服務器等聞名應用上。任何開發多線程應用程序的開發人員都必須對他們的同步概念特別清楚。不是為了讓每個對象都是線程安全的而導致系統不堪重負,而是應該關注死鎖情況並在程序設計之初就解決盡可能多的死鎖問題。理解同步帶來的性能瓶頸問題同樣很重要,因為它將影響應用程序的總體性能。