C#設計模式-享元模式

前言

最近開始花點心思研究下設計模式,主要仍是讓本身寫的代碼可重用性高、保證代碼可靠性。所謂設計模式,我找了下定義:是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構同樣。數據庫

爲何要提倡「Design Pattern(設計模式)」?設計模式

根本緣由是爲了代碼複用,增長可維護性。所以此次咱們來學習下設計模式,最後會經過C#語言來實現這些設計模式做爲例子,深入理解其中的精髓。安全

定義

享元模式:它使用共享物件,用來儘量減小內存使用量以及分享資訊給儘量多的類似物件;它適合用於只是因重複而致使使用沒法使人接受的大量內存的大量物件。一般物件中的部分狀態是能夠分享。常見作法是把它們放在外部數據結構,當須要使用時再將它們傳遞給享元。服務器

以前講到的單例模式,一個類只有一個惟一的對象,也就是說,無論new多少次,只須要建立這個類的一個對象,若是不採用單例模式,每new一次,就會建立一個對象,便會產生大量重複的對象,而這些對象要消耗很大的資源的時候,是會產生資源互搶的延遲的。這個時候享元模式就能幫忙解決這類的問題。數據結構

特色

享元模式的意圖是經過共享有效支持大量細粒度的對象,來提供應用程序的性能,節省系統中重複建立對象實例的性能消耗,這個怎麼理解呢?其實就是如下幾點的含義:多線程

一、當咱們系統中某個對象類型的實例較多的狀況。併發

二、而且要求這些實例進行分類後,發現真正有區別的分類不多的狀況。ide

例如咱們的生活中不少的場景,咱們在使用拼音輸入的法的時候,若是說咱們每一個字都是new一個對象實例的操做的話,那麼內存中的實例就太可怕,這個時候,咱們是否是能夠考慮將這些重複的字體在內存中只是建立一次,而是經過複用對象的形式,來組織一些可能有多個字符重複的內容呢?函數

優缺點

優勢:

1、性能

下降了系統中對象的數量,從而下降了系統中細粒度對象給內存帶來的壓力。

缺點:

1、

爲了使對象能夠共享,須要將一些狀態外部化,這使得程序的邏輯更復雜,使系統複雜化。

2、

享元模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。

實現思路

 

 

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 服務器等聞名應用上。任何開發多線程應用程序的開發人員都必須對他們的同步概念特別清楚。不是爲了讓每一個對象都是線程安全的而致使系統不堪重負,而是應該關注死鎖狀況並在程序設計之初就解決儘量多的死鎖問題。理解同步帶來的性能瓶頸問題一樣很重要,由於它將影響應用程序的整體性能。

總結

前面單例模式的最後,我舉了個我項目中用到的一個例子,其實那也就是數據庫程序鏈接池的示例,若是採用單例模式,整個系統就只使用一個數據庫鏈接,當用戶併發量很大的時候,系統就會變得很慢,由於創建數據庫鏈接時一個耗費資源的活動,對於一次或幾回的數據庫鏈接,感受不到什麼影響,而對於大型門戶網站,頻繁的進行數據庫鏈接操做必然會佔了不少資源,網站的相應速度就會很慢,甚至致使服務器奔潰,理論上是如此,然而個人一個項目目前就用到這個單例模式,用戶的併發量估計會達到好幾千,所以在考慮是否須要改進,而這種對比也只是理論上的比較,沒有具體的經歷和數據來肯定這種單例就會致使系統卡頓,所以若是對這方面有必定研究的朋友能夠給我一些數據來告訴到底會不會出現這類問題。

相關文章
相關標籤/搜索