天天看點

【愚公系列】2021年12月 二十三種設計模式(五)-單例模式(Singleton Pattern)

文章目錄

前言

一、單例模式(Singleton Pattern)

二、使用步驟

角色

示例

總結

優點

缺點

使用場景

設計模式(Design pattern)是一套被反複使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人了解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多赢的,設計模式使代碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。

提示:以下是本篇文章正文内容,下面案例可供參考

單例模式屬于建立型模式,保證一個類僅有一個執行個體,并提供一個通路它的全局通路點。

一個類有且僅有一個執行個體,并且自行執行個體化向整個系統提供。

1、單例類(Singleton)

保證唯一并提供全局通路點的單例類。

【愚公系列】2021年12月 二十三種設計模式(五)-單例模式(Singleton Pattern)
命名空間SingletonPattern中包含7個單例類,本案例将介紹這7種常見的單例實作方法。

public sealed class Singleton {

    private static Singleton _instance = null;

    public static Singleton GetInstance() {
        if(_instance == null) {
            _instance = new Singleton();
            Console.WriteLine("Singleton.GetInstance()!");
        }
        return _instance;
    }

}      

最常見的單例類,但是無法保證線程安全。因為首次運作時,n個線程可同時到達if(_instance == null),導緻_instance可能會被多初始化n-1次(有1次是需要初始化的)。在_instance被初始化之後新啟動的線程不會使該情況重制。

public sealed class SingletonSafe {

    private static SingletonSafe _instance = null;

    private static readonly object _lock = new object();

    public static SingletonSafe GetInstance() {
        lock (_lock) {
            if (_instance == null) {
                _instance = new SingletonSafe();
                Console.WriteLine("SingletonSafe.GetInstance()!");
            }
        }
        return _instance;
    }

}      

使用私有靜态object類型的鎖(微軟推薦),lock關鍵字會占有該鎖,之後請求該鎖的其它線程必需等待其釋放才能進入。該方法可實作線程安全的單例模式,但是鎖屬于昂貴資源,“占有鎖”和“釋放鎖”都比較耗時,并會在一定程度上阻止其它線程的執行,會顯著影響程式的并發性,是以有了下面的優化。

public sealed class SingletonSafe2 {

    private static SingletonSafe2 _instance = null;

    private static readonly object _lock = new object();

    public static SingletonSafe2 GetInstance() {
        if (_instance == null) {
            lock (_lock) {
                if (_instance == null) {
                    _instance = new SingletonSafe2();
                    Console.WriteLine("SingletonSafe2.GetInstance()!");
                }
            }
        }
        return _instance;
    }

}      

通過優先使用if (_instance == null)這種耗費資源較少的比較來決定是否進入鎖,可大幅度提高性能。因為_instance不為null時,直接傳回即可。

public sealed class SingletonLazy {

    private static readonly Lazy<SingletonLazy> _instance =
        new Lazy<SingletonLazy>(() => {
            Console.WriteLine("SingletonLazy.GetInstance()!");
            return new SingletonLazy();
        });

    public static SingletonLazy GetInstance() {
        return _instance.Value;
    }

}      

帶泛型的Lazy式單例實作,這是線程安全的,僅提供給大家參考。

public sealed class SingletonReadOnly {

    private static readonly SingletonReadOnly _instance =
        new SingletonReadOnly();

    public SingletonReadOnly() {
        Console.WriteLine("SingletonReadOnly.GetInstance()!");
    }

    public static SingletonReadOnly GetInstance() {
        return _instance;
    }

}      

靜态隻讀式單例實作(由運作時保證唯一),這是線程安全的,僅提供給大家參考。

public abstract class SingletonGenericBase<T> where T : class, new() {

    private static T _instance = null;

    private static readonly object _lock = new object();

    public static T GetInstance() {
        if (_instance == null) {
            lock (_lock) {
                if (_instance == null) {
                    _instance = new T();
                    Console.WriteLine("SingletonGeneric.GetInstance()!");
                }
            }
        }
        return _instance;
    }

}

public sealed class SingletonGeneric : SingletonGenericBase<Singleton> {

    public SingletonGeneric() { }

}      

複雜的泛型實作,這是線程安全的,僅提供給大家參考。

public abstract class SingletonGenericBase2<T> where T : class {

    private static readonly Lazy<T> _instance = new Lazy<T>(() => {
        var ctors = typeof(T).GetConstructors(
            BindingFlags.Instance
            | BindingFlags.NonPublic
            | BindingFlags.Public);

        if (ctors.Count() != 1)
            throw new InvalidOperationException(
                String.Format("Type {0} must have exactly one constructor.",
                              typeof(T)));

        var ctor = ctors.SingleOrDefault(
            c => !c.GetParameters().Any() && c.IsPrivate);

        if (ctor == null)
            throw new InvalidOperationException(
                String.Format("The constructor for {0} must be private and take no parameters.",
                              typeof(T)));

        Console.WriteLine("SingletonGeneric2.GetInstance()!");
        return (T)ctor.Invoke(null);
    });

    public static T GetInstance() {
        return _instance.Value;
    }

}

public sealed class SingletonGeneric2 : SingletonGenericBase2<SingletonGeneric2> {

    private SingletonGeneric2() { }

}      
public class Program {

    public static void Main(string[] args) {
        var singleton = Singleton.GetInstance();
        singleton = Singleton.GetInstance();

        var singletonSafe = SingletonSafe.GetInstance();
        singletonSafe = SingletonSafe.GetInstance();

        var singletonSafe2 = SingletonSafe2.GetInstance();
        singletonSafe2 = SingletonSafe2.GetInstance();

        var singletonReadOnly = SingletonReadOnly.GetInstance();
        singletonReadOnly = SingletonReadOnly.GetInstance();

        var singletonLazy = SingletonLazy.GetInstance();
        singletonLazy = SingletonLazy.GetInstance();

        var singletonGeneric = SingletonGeneric.GetInstance();
        singletonGeneric = SingletonGeneric.GetInstance();

        var singletonGeneric2 = SingletonGeneric2.GetInstance();
        singletonGeneric2 = SingletonGeneric2.GetInstance();

        Console.ReadKey();
    }

}      

以上是調用方的代碼,每個GetInstance方法均調用2次以展示效果。以下是這個案例的輸出結果:

Singleton.GetInstance()!
SingletonSafe.GetInstance()!
SingletonSafe2.GetInstance()!
SingletonReadOnly.GetInstance()!
SingletonLazy.GetInstance()!
SingletonGeneric.GetInstance()!
SingletonGeneric2.GetInstance()!      

1、單例模式會阻止其他對象執行個體化其自己的單例對象的副本,進而確定所有對象都通路唯一執行個體;

2、因為類控制了執行個體化過程,是以類可以靈活更改執行個體化過程。

1、沒有接口,不能繼承,與單一職責原則沖突。

1、需要頻繁的進行建立和銷毀的對象;

2、建立對象時耗時過多或耗費資源過多,但又經常用到的對象;

3、工具類對象;

4、頻繁通路資料庫或檔案的對象。