侧边栏壁纸
博主头像
修恩的 Paradeisos博主等级

παράδεισος

  • 累计撰写 20 篇文章
  • 累计创建 4 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

設計模式 : 單例模式

Ashun
2023-11-08 / 0 评论 / 0 点赞 / 121 阅读 / 3537 字

前言

昨天在寫 WPF應用 的時候,遇到了一個 static 的問題。呼叫方要求 function 必須是 static,但偏偏這個 function 又不太好直接 static。

在一通折騰之後,順便從同事那裡學到一個新招: 單例模式


單例模式

單例模式是一種設計模式,目的是用來確保在一定情況下只有一個實例被建立,並且開放一個全局接口讓外部使用這個實例。如此一來就不用到處在各個地方 new 一個實例出來,不但消耗資源,某些情況下也不好管理。

以下用 C# 做實作。


簡單實作

假設我寫了一個類別(MessageControl),用於控制訊息的各種操作。

public class MessageControl
{
    public MessageControl() { }
    public void PrintMessage()
    {
        Console.WriteLine("Hello, I am Singleton");
    }
}

一般來說如果要使用這個 class,就必須為其 new 一個實體。然後再呼叫裡面的 PrintMessage()。

MessageControl msgctrl = new MessageControl();
msgctrl.PrintMessage();

但是,如果我要在各個地方呼叫 PrintMessage(),很多時候就必須再 new 一個實例出來,最終就會有一堆實例,並且他們做的事情可能都是一樣的。

要解決這個問題,就可以使用單例模式。讓實例只需在 class 內部建立一次,並開放一個全局接口取用,並且每次取用時先判斷實例是否已建立,已經建立的話就直接 return 建立好的那一個就可以了。

public class MessageControl
{
    private static MessageControl instance; // 宣告實例
    private MessageControl() { }
    public static MessageControl GetInstance() // 全局接口
    {
        if (instance == null) { instance = new MessageControl(); } // 若未建立過,就建立實例
        return instance;
    }

    // Method function
    public void PrintMessage()
    {
        Console.WriteLine("Hello, I am Singleton");
    }
}

如此一來,不管在任何地方,都只要呼叫此類別的 GetInstance(),他會幫我們視情況建立唯一的自己的實例。之後就可以安心地使用這個類別了。

MessageControl.GetInstance().PrintMessage();

「懶漢式」與「餓漢式」

上一段的例子屬於「懶漢式」(Lazy Initialization)。只有在初次使用的時候才會建立實例。

那我們可不可以在程式初始化的時候就將實例先建立出來? 當然可以,此情況也被稱為「餓漢式」(Eager Initialization)。懶漢比較懶,餓漢則比較積極的感覺。

public class MessageControl
{
    private static MessageControl instance = new MessageControl();
    private MessageControl() { }
    public static MessageControl GetInstance()
    {
        return instance;
    }

    // Method function
    // ......
}

就真的只是把 new MessageControl() 在宣告實例時一併建立而已。


雙重檢查鎖定(Double-Check Locking)

雖然說懶漢式可以在需要時才建立實例,但會有一個問題,那就是在多線程的情況下,若有同時來了兩個訪問,那就麻煩了,因此我們需要 「雙重檢查鎖定」(Double-Check Locking)。

public class MessageControl
{
    private static MessageControl instance;
    private static readonly object lockObject = new object();
    private MessageControl() { }
    public static MessageControl GetInstance()
    {
        if (instance == null)
        {
            lock (lockObject)
            {
                if (instance == null)
                {
                    instance = new MessageControl();
                }
            }
        }
        return instance;
    }

    // Method function
    // ......
}

如此一來 lockObject 可以確保 lock 區域一次只會有一個線程進入。


靜態內部類

如果希望將內部靜態封裝(Static Inner Class),對外部進行隱藏的話,也可以。

public class MessageControl
{
    // private 防止在外部實例化
    private MessageControl() { }
    private static class SingletonHolder{
        internal static readonly MessageControl INSTANCE = new MessageControl();
    } 
    public static MessageControl GetInstance()
    {
        return SingletonHolder.INSTANCE;
    }

    // Method function
    // ......
}

以上。

评论区