設計模式 單例模式詳解

2021-10-04 02:42:42 字數 2993 閱讀 3350

單例模式(singleton),保證乙個類僅有乙個例項,並提供乙個訪問它的全域性訪問點。

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

我們知道,oop 的四大特性是封裝、抽象、繼承、多型。單例這種設計模式對於其中的抽象、繼承、多型都支援得不好。一般單例模式都會違反「基於介面而非實現的設計原則」,也就違背了廣義上理解的 oop 的抽象特性。

我們知道,**的可讀性非常重要。在閱讀**的時候,我們希望一眼就能看出類與類之間的依賴關係,搞清楚這個類依賴了哪些外部類。通過建構函式、引數傳遞等方式宣告的類之間的依賴關係,我們通過檢視函式的定義,就能很容易識別出來。但是,單例類不需要顯示建立、不需要依賴引數傳遞,在函式中直接呼叫就可以了。如果**比較複雜,這種呼叫關係就會非常隱蔽。在閱讀**的時候,我們就需要仔細檢視每個函式的**實現,才能知道這個類到底依賴了哪些單例類。

我們知道,單例類只能有乙個物件例項。如果未來某一天,我們需要在**中建立兩個例項或多個例項,那就要對**有比較大的改動。

你可能會說,會有這樣的需求嗎?既然單例類大部分情況下都用來表示全域性類,怎麼會需要兩個或者多個例項呢?實際上,這樣的需求並不少見。我們拿資料庫連線池來舉例解釋一下。在系統設計初期,我們覺得系統中只應該有乙個資料庫連線池,這樣能方便我們控制對資料庫連線資源的消耗。

所以,我們把資料庫連線池類設計成了單例類。但之後我們發現,系統中有些 sql 語句執行得非常慢。這些 sql 語句在執行的時候,長時間占用資料庫連線資源,導致其他 sql 請求無法響應。為了解決這個問題,我們希望將慢 sql 與其他 sql 隔離開來執行。為了實現這樣的目的,我們可以在系統中建立兩個資料庫連線池,慢 sql 獨享乙個資料庫連線池,其他 sql 獨享另外乙個資料庫連線池,這樣就能避免慢 sql 影響到其他 sql 的執行。

單例模式的使用會影響到**的可測試性。如果單例類依賴比較重的外部資源,比如 db,我們在寫單元測試的時候,希望能通過 mock 的方式將它替換掉。而單例類這種硬編碼式的使用方式,導致無法實現 mock 替換。

本文實現**均基於c#。一般實現方式有兩種:餓漢式和懶漢式。由於餓漢式,即靜態初始化的方式,它是類一載入就例項化的物件,所以要提前占用系統資源。然而懶漢式,又會面臨著多執行緒訪問的安全性問題,需要做類似雙重鎖定的處理才可以保證安全。所以到底使用哪一種方式,取決於實際的需求。

/// 

/// 單例模式-雙重鎖定

///

public

class

singleton

//私有的建構函式,防止外部程式通過new來例項化它

public

static

singleton

instance()

}}return instance;

}}

/// 

/// 單例模式-靜態內部類

///

public

class

singleton2

private

static

class

singletoninstance

public

static

singleton2

getinstance()

}

/// 

/// 靜態初始化

///

public

sealed

class

singleton3

public

static

singleton3

getinstance()

}

單例模式的定義中提到,「乙個類只允許建立唯一乙個物件」。那物件的唯一性的作用範圍是什麼呢?是指執行緒內只允許建立乙個物件,還是指程序內只允許建立乙個物件?答案是後者,也就是說,單例模式建立的物件是程序唯一的。

程序之間是不共享位址空間的,如果我們在乙個程序中建立另外乙個程序(比如,**中有乙個 fork() 語句,程序執行到這條語句的時候會建立乙個新的程序),作業系統會給新程序分配新的位址空間,並且將老程序位址空間的所有內容,重新拷貝乙份到新程序的位址空間中,這些內容包括**、資料(比如 user 臨時變數、user 物件)。

所以,單例類在老程序中存在且只能存在乙個物件,在新程序中也會存在且只能存在乙個物件。而且,這兩個物件並不是同乙個物件,這也就說,單例類中物件的唯一性的作用範圍是程序內的,在程序間是不唯一的。

我們通過乙個 map 來儲存物件,其中 key 是執行緒 id,value 是物件。這樣我們就可以做到,不同的執行緒對應不同的物件,同乙個執行緒只能對應乙個物件。

首先,我們還是先來解釋一下,什麼是「集群唯一」的單例。我們還是將它跟「程序唯一」「執行緒唯一」做個對比。「程序唯一」指的是程序內唯

一、程序間不唯一。「執行緒唯一」指的是執行緒內唯

一、執行緒間不唯一。集群相當於多個程序構成的乙個集合,「集群唯一」就相當於是程序內唯

一、程序間也唯一。也就是說,不同的程序間共享同乙個物件,不能建立同乙個類的多個物件。

我們需要把這個單例物件序列化並儲存到外部共享儲存區(比如檔案)。程序在使用這個單例物件的時候,需要先從外部共享儲存區中將它讀取到記憶體,並反序列化成物件,然後再使用,使用完成之後還需要再儲存回外部共享儲存區。為了保證任何時刻在程序間都只有乙份物件存在,乙個程序在獲取到物件之後,需要對物件加鎖,避免其他程序再將其獲取。在程序使用完這個物件之後,需要顯式地將物件從記憶體中刪除,並且釋放對物件的加鎖。

跟單例模式概念相對應的還有乙個多例模式。那如何實現乙個多例模式呢?「單例」指的是,乙個類只能建立乙個物件。對應地,「多例」指的就是,乙個類可以建立多個物件,但是個數是有限制的,比如只能建立 3 個物件。

多例的實現也比較簡單,通過乙個 map 來儲存物件型別和物件之間的對應關係,來控制物件的個數。

設計模式 單例模式詳解

1.懶漢式,執行緒不安全 public class singletonpattern 懶漢式,執行緒不安全 private static singletonpattern instance public static singletonpattern getinstance return insta...

單例設計模式(詳解)

模式 模式就是解決一類問題的固定步驟 單例設計模式 保證乙個類在記憶體中只有乙個物件 舉例 多個瀏覽器向伺服器傳送請求,只建立乙個servlet物件處理相應的請求,而不是每接收乙個請求,就建立乙個servlet物件 1.餓漢單例設計模式 1.私有化建構函式 2.宣告本類的引用型別變數,並且使用該變數...

設計模式詳解(1) 單例模式

設計模式詳解 1 單例模式 在有些系統中,為了節省記憶體資源 保證資料內容的一致性,對某些類要求只能建立乙個例項,這就是所謂的單例模式。單例 singleton 模式的定義 指乙個類只有乙個例項,且該類能自行建立這個例項的一種模式。例如,windows 中只能開啟乙個任務管理器,這樣可以避免因開啟多...