1. 程式人生 > 其它 >取到list裡面某個特定的物件 預處理出來一個簡單對映

取到list裡面某個特定的物件 預處理出來一個簡單對映

技術標籤:【設計模式】設計模式單例模式

目錄

一、定義:

二、特點:

三、優點:

四、缺點:

五、實現:

六、餓漢單例和懶漢單例區別


一、定義:

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

通常我們可以讓一個全域性變數使得一個物件被訪問,但它不能防止你例項化多個物件。一個最好的辦法就是,讓類自身負責儲存他的唯一例項。這個類可以保證沒有其他例項可以被建立,並且它可以提供一個訪問該例項的方法。

二、特點:

1.保證唯一例項物件;

2.該單例物件必須由單例類自行建立;

3.單例類對外提供一個訪問該單例的全域性訪問點;

image.png

三、優點:

1.保證唯一的例項化

2.單例模式因為Singleton類封裝它的唯一例項,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一例項的受控訪問

四、缺點:

1.單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則;

2.單例模式一般沒有介面,擴充套件困難。如果要擴充套件,則除了修改原來的程式碼,沒有第二種途徑,違背開閉原則;

3.單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則

五、實現:

①、單例模式

//Single類,定義一個GetInstance操作,允許客戶訪問它的唯一例項。GetInstance是一個靜態方法,主要負責建立自己的唯一例項
class LazySingleton
{
    //懶漢單例:在第一次被引用時,才會將自己例項化。
    private static LazySingleton instance;      //私有靜態的類變數
    private LazySingleton() { }                 //私有的構造方法,防止外界能夠new例項化它

    public static LazySingleton GetInstance()    //獲得例項靜態方法
    {
        if (instance == null)                   //判斷是否有例項
        {
            instance = new LazySingleton();     //建立例項
        }
        return instance;                        //返回例項
    }
}


//客戶端程式碼
class Program
{
    static void Main(string[] args)
    {
        Singleton s1 = Singleton.GetInstance();
        Singleton s2 = Singleton.GetInstance();
        
        if (s1 == s2)                         //比較兩次例項化後物件的結果是否相同
        {
            Console.WriteLine("兩個物件是相同的例項");    
        }
        Console.Read();    
    }
}

這樣的話就滿足了單例的效果,保證只例項化一個類,客戶端通過那唯一可以訪問的GetInstance方法來訪問那一個例項。但如果是多個執行緒同時呼叫GetInstance方法,同時執行到了GetInstance方法這兒,它們都會去判斷有沒有被例項化,判斷都為True,那樣的話就建立了兩個例項,就違背了單例模式,不是一個單例。如何解決這樣一個問題呢?

②、多執行緒單例

//單例類
class MultiThreadSingleton
{
    private static MultiThreadSingleton instance;             //宣告靜態類變數
   
    private static readonly object syncRoot = new object();   //程式執行時建立一個靜態只讀的程序輔助物件
    private MultiThreadSingleton() { }                       //私有的構造方法,防止外界new例項化它

    public static MultiThreadSingleton GetInstance()         //建立例項方法
    {
        lock (syncRoot)                                      //加鎖,在同一個時刻加了鎖的那部分程式只有一個執行緒可以進入
        {
            if (instance == null)                           //判斷是否建立例項
            {
                instance = new MultiThreadSingleton();      //建立例項
            }
        }
        return instance;                                     //返回例項
    }
}

//客戶端程式碼
class Program
{
    static void Main(string[] args)
    {
        MultiThreadSingleton s1 = MultiThreadSingleton.GetInstance();   //呼叫單例類的GetInstance方法,獲得例項
        MultiThreadSingleton s2 = MultiThreadSingleton.GetInstance();   //呼叫單例類的GetInstance方法,獲得例項
        
        if (s1 == s2)                                 //判斷兩個例項是否相同
        {
            Console.WriteLine("兩個物件是相同的例項");  //控制檯輸出結果
        }
        Console.Read();                               //讀取下一個字元                  
    }
}

我們可以讓最先進入的那個執行緒先上一把鎖,建立例項。後面在進入的執行緒就不會再去建立物件例項了,因為第一名來的執行緒已經建立了,你這個判斷的結果是False,自然無法建立了。這樣的話就保證了多個執行緒同時訪問的話不會有多個例項化。解決了上面單例項帶來的問題。但每次進入的執行緒都需要先加鎖在判斷,我都還不知道有沒有建立過這個例項呢你就讓我加鎖,第一名已經例項化過了,我進去再加鎖,在判斷一次,如果有上百個執行緒同時訪問呢,這樣的工作重複上百次,不是很影響我這個程式的效能嗎?我們就可以用到雙重鎖定來解決這個問題

③、雙重鎖定

class MultiThreadSingleton
{
    private static MultiThreadSingleton instance;             //宣告靜態類變數
   
    private static readonly object syncRoot = new object();   //程式執行時建立一個靜態只讀的程序輔助物件
    private MultiThreadSingleton() { }                        //私有的構造方法,防止外界new例項化它

    public static MultiThreadSingleton GetInstance()          //此方法是獲得本類例項的唯一全域性訪問點
    {
        if(instance ==null)                                   //一重:判斷是否建立例項
        {
            lock (syncRoot)                                      //加鎖,在同一個時刻加了鎖的那部分程式只有一個執行緒可以進入
            {
                if (instance == null)                            //二重:判斷若例項不存在,則new一個新例項,否則返回已有的例項
                {
                    instance = new MultiThreadSingleton();       //建立例項
                }
            }
         }
        return instance;                                         //返回例項
    }
}

//客戶端程式碼
class Program
{
    static void Main(string[] args)
    {
        MultiThreadSingleton s1 = MultiThreadSingleton.GetInstance();   //呼叫單例類的GetInstance方法,獲得例項
        MultiThreadSingleton s2 = MultiThreadSingleton.GetInstance();   //呼叫單例類的GetInstance方法,獲得例項
        
        if (s1 == s2)                                 //判斷兩個例項是否相同
        {
            Console.WriteLine("兩個物件是相同的例項");  //控制檯輸出結果
        }
        Console.Read();                               //讀取下一個字元                  
    }
}

通過這樣兩重的判斷,進入的執行緒不用每次都加鎖,只是在例項未被建立的時候在加鎖處理。同時也保證多執行緒的安全。

④、靜態初始化

C#與公共語言執行庫也提供了一種‘靜態初始化’方法,這種方法不需要開發人員顯示地編寫執行緒安全程式碼,即可解決多執行緒環境下它是不是安全問題。也可以理解為餓漢單例。這種靜態初始化的方式是在自己被載入時將自己例項化,有種迫不及待的感覺

public sealed class EagerSingleton       //組織發生派生,而派生可能會增加例項(sealed:密封的)
    {
        //在第一次引用類的任何成員時建立例項。公共語言執行庫負責處理變數初始化
        private static readonly EagerSingleton instance = new EagerSingleton();    //餓漢單例:類一載入就例項化物件,提前佔用系統資源
        private EagerSingleton() { } 
        public static EagerSingleton GetInstance()
        {
            return instance;
        }
    }    

六、餓漢單例和懶漢單例區別:

例項化

執行緒安全

懶漢單例

被引用時

不安全(通過雙重鎖定保障)

餓漢單例

類一載入時

安全