2017年9月3日 星期日

.NET 如何在 C#,使用單例模式 (Singleton Pattern),設計這樣模式的類別

單例模式是設計模式(Design Patterns)的其中一種,在這裡,我們將透過底下程式碼,展示如何實作出單例模式的類別,並且檢測這樣的設計是否正確可以運行。


了解更多關於 [C# 程式設計手冊 




展示專案原始碼

    /// <summary>
    /// 單例模式 (Singleton Pattern) 的類別設計展示
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 這個欄位為私有,用來持有唯一的執行實例(Instance)
        /// </summary>
        private static Singleton instance;

        public DateTime GenerateTime { get; set; }

        /// <summary>
        /// 建構式,可以在這裡做是到的實例物件的初始化動作
        /// 不過,這樣預設建構式的存取修飾詞為 private,也就是,讓何人都無法使用 new 運算子產生這個類別的物件,只有他自己本身可以
        /// </summary>
        private Singleton()
        {
            GenerateTime = DateTime.Now;
        }

        /// <summary>
        /// 取得這個類別的單例物件 (Singleton Instance)的屬性設計說明
        /// 這個屬性是唯讀,也就是只有讀取功能,無法做任何設定(因為沒有 set 屬性存取子)
        /// 當然,您也可以使用一個靜態方法,取得這個類別的單例物件
        /// </summary>
        public static Singleton Instance
        {
            get
            {
                // 若 instance 並沒有持有一個單例物件,則需要在這個時候,進行產生出來
                // ?? 若這個 單例物件 需要能夠在多執行緒環境下正確執行,又該如何設計呢?
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("請稍後四秒鐘,正在產生四個單例物件中...");

            // 取得這個類別的單例物件
            Singleton singleton1 = Singleton.Instance;
            Thread.Sleep(1000);
            Singleton singleton2 = Singleton.Instance;
            Thread.Sleep(1000);
            Singleton singleton3 = Singleton.Instance;
            Thread.Sleep(1000);
            Singleton singleton4 = Singleton.Instance;
            Thread.Sleep(1000);

            Console.WriteLine($"單例物件1 HashCode: {singleton1.GetHashCode()} {singleton1.GenerateTime} 的產生時間識別");
            Console.WriteLine($"單例物件2 HashCode: {singleton2.GetHashCode()} {singleton2.GenerateTime} 的產生時間識別");
            Console.WriteLine($"單例物件3 HashCode: {singleton3.GetHashCode()} {singleton3.GenerateTime} 的產生時間識別");
            Console.WriteLine($"單例物件4 HashCode: {singleton4.GetHashCode()} {singleton4.GenerateTime} 的產生時間識別");

            if(singleton1 == singleton2 )
            {
                if (singleton2 == singleton3)
                {
                    if (singleton3 == singleton4)
                    {
                        Console.WriteLine($"這四個物件變數,指向同一個物件記憶體位置的單例物件");
                    }
                }
            }

            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }

單例模式設計說明

在這裡我們說明這個單例模式的類別該如何設計:
  • 私有 Private 建構式
    在單例模式中,使用這將無法使用 new 運算子來產生這個類別的物件,我們必須將這樣的需求給他禁止,因此,我們在預設建構函式的存取修飾詞(Access Modifier)設定成為 private
        private Singleton()
        {
            GenerateTime = DateTime.Now;
        }
  • 使用屬性來取得唯一的執行實體(Instance)
    接著,我們建立一個 Instance 屬性與 instance 私有欄位,使用者僅能夠透過這個 Instance 屬性來取得這個類別的執行實體,而且,這個屬性屬於唯讀,也就是說,使用者沒有任何權力來由外部設定這個 instance 私有欄位的內容。
    在這個 get屬性存取子中,我們會先判斷私有欄位 instance 這個物件欄位變數是否有任何物件存在,若該物件欄位變數為空值 (null),我們將會使用 instance = new Singleton(); 這樣的敘述,產生這個單例物件。在這裡,我們看到了我們使用了 new 運算子呼叫了預設建構函式,我們可以這樣做,這是因為,我們是在這個類別裡面的 get屬性存取來執行這樣的敘述,這是可以的,因為,預設建構函式設定其存取子修飾詞為 private,是限制不允許從類別以外的地方來呼叫這個建構函式,但是,卻必須可以在該類別裡面來呼叫這個私有的建構函式。
        public static Singleton Instance
        {
            get
            {
                // 若 instance 並沒有持有一個單例物件,則需要在這個時候,進行產生出來
                // ?? 若這個 單例物件 需要能夠在多執行緒環境下正確執行,又該如何設計呢?
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }

底下則是這個展示範例專案的執行結果,我們可以看的出來,透過 Singleton.Instance 所取得的物件,都是同一個。
請稍後四秒鐘,正在產生四個單例物件中...
單例物件1 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件2 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件3 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
單例物件4 HashCode: 46104728 2017/9/3 下午 10:17:24 的產生時間識別
這四個物件變數,指向同一個物件記憶體位置的單例物件
Press any key for continuing...

了解更多關於 [C# 程式設計手冊 





沒有留言:

張貼留言