.Net設計模式之單例模式


單例模式的介紹

在軟件的開發過程中,很多時候,我們需要對一個類進行實例化后,再使用,有時這個類比較簡單,有時也可能會很復雜,但不管怎樣,為了保證軟件的質量和效率,大多數時候,我們只希望它被實例化一次,所以這就需要引入單例模式(Singleton Pattern)了。單例模式,即保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。那,怎么做到呢?為不不被實例化多個對象,就可以讓類自身負責保存它的唯一實例。軟件中重復使用某個類時,為了防止多次實例化產生的資源消耗,這個時候就應該使用單例設計模式了。如:網絡請求、數據庫操作等。


上面為單例的類圖

單例要點

  1. 構造函數不對外開放,一般為 private;
  2. 通過一個靜態方法或者枚舉返回單例對象;
  3. 確保單例類的對象有且只有一個,尤其是在多線程環境下;
  4. 確保單例類對象在反序列化時不會被重新構建對象。

代碼示例

簡單實現

    public class Singleton
    {
        private static Singleton instance;

        /// <summary>
        /// 構造方法為private,可以僻免外界通過利用new創建此類實例的可能
        /// </summary>
        private Singleton() { }

        /// <summary>
        /// 此方法為獲得本類實例的唯一全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance() 
        {
            if (instance == null)
            {
                return instance;
            }
            return instance;
        }
    }

上面的寫法,可以實現單例模式,在單線程的情況下,並沒有問題,但如果在多線程的環境下,兩個或更多的線程同時判斷 instance == null 為 true, 那么這個類仍然可以被多次實例化,那么它是不安全的,並不是真正的單例。這個時候,我們就會想到,可以在這之前,加上 lock 解決問題。提示代碼如下:

線程安全

    public class Singleton
    {
        private static readonly object locker = new object();
        private static Singleton instance;

        /// <summary>
        /// 構造方法為private,可以僻免外界通過利用new創建此類實例的可能
        /// </summary>
        //private Singleton() { }

        /// <summary>
        /// 此方法為獲得本類實例的唯一全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance() 
        {
            lock (locker)
            {
                if (instance == null)
                {
                    return instance;
                }
            }
            return instance;
        }
    }

lock( locker) // 在同一時刻加了鎖的那部分程序,只有一個線程可以進入,其他線程要進入,只能待已進行的線程退出lock程序塊后,才能進入,這就保證了線程安全,不會創建多個實例。現在線程安全是解決了,但還是有個小問題,每次獲取實例的時候,都需要加鎖,然后才能判斷實例是否被創建了,怎么解決這個問題?這就需要再引入一個 雙重鎖 的做法了。

雙重鎖定

    /// <summary>
    /// 
    /// </summary>
    public class Singleton
    {
        private static readonly object locker = new object();
        private static Singleton instance;

        /// <summary>
        /// 構造方法為private,可以僻免外界通過利用new創建此類實例的可能
        /// </summary>
        //private Singleton() { }

        /// <summary>
        /// 此方法為獲得本類實例的唯一全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance() 
        {
            // 判斷1
            if (instance == null)
            {
                lock (locker)
                {
                    // 判斷2
                    if (instance == null)
                    {
                        return instance;
                    }
                }
            }
            return instance;
        }
    }


上面的代碼,在lock(locker) 前后,都加入了 對實例是否創建的判斷, 判斷1 主要是考慮到在實例已經被創建后,僻免程序再鎖定 后面的程序塊,減少資源的浪費,判斷2 是為了 在沒有創建實例的情況下才創建實例。經過多次改造后的單例,看似完美,但還是在獲取實例的時候需要去判斷實例是不是被創建了,那么,有沒有別的方式每次都去判斷呢?答案是有的,C#與公共語言運行庫也提供了一種 "靜態初始化" 方法,這種方法不需要開發人員顯式地編寫線程安全代碼。

靜態初始化

    /// <summary>
    /// 這里用到了 sealed 主要是不讓其他類繼承,而繼承可能會增加實例
    /// </summary>
    public sealed class Singleton
    {
        //在第一次引用類的任何成員時創建實例,由公共語言運行庫負責處理變量的初始化
        private static readonly Singleton instance = new Singleton();

        //構造函數私有化,是為了不被通過 new 達到實例化的目的
        private Singleton() { }

        public static Singleton GetSingleton() {
            return instance;
        }
    }

這個實現方式與前面的示例類似,都是解決了單例模式試圖解決的兩個基本問題:全局訪問和實例化控制,公共靜態屬性是為訪問實例提供了一個全局訪問點。不同的地方在於它依賴公共語言運行庫來初始化變量。再就是它的構造方法標記為私有,所以不能在類本身以外的地方通過 new 來實例化 Singleton 類;因此變量引用的是可以在系統中存在唯一的實例。值得注意的是,instance 變量標記為 readonly,也就是只能在靜態初始化期間 或 在類的構造函數中分配變量。關於 sealed 的關鍵字,也可以在上面幾個類中使用,只是類可能要稍加改動。

最后,感覺各位能花時間看到這里,謝謝,歡迎留言交流。

參考文獻:

大話設計模式 程傑 著


免責聲明!

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



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