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

總結

前面單例模式的最后,我舉了個我項目中用到的一個例子,其實那也就是數據庫程序連接池的示例,如果采用單例模式,整個系統就只使用一個數據庫連接,當用戶並發量很大的時候,系統就會變得很慢,因為建立數據庫連接時一個耗費資源的活動,對於一次或幾次的數據庫連接,感覺不到什么影響,而對於大型門戶網站,頻繁的進行數據庫連接操作必然會占了很多資源,網站的相應速度就會很慢,甚至導致服務器奔潰,理論上是如此,然而我的一個項目目前就用到這個單例模式,用戶的並發量估計會達到好幾千,因此在考慮是否需要改進,而這種對比也只是理論上的比較,沒有具體的經歷和數據來確定這種單例就會導致系統卡頓,因此如果對這方面有一定研究的朋友可以給我一些數據來告訴到底會不會出現這類問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM