網站首頁 編程語言 正文
單例模式的概念
單例模式很容易記住。就像名稱一樣,它只能提供對象的單一實例,保證一個類只有一個實例,并提供一個全局訪問該實例的方法。
在第一次調用該實例時被創建,然后在應用程序中需要使用該特定行為的所有部分之間重復使用。
單例模式結構
單例模式的使用場景
你會在許多不同的情況下使用單例模式。比如:
- 當你想使用同一個數據庫連接來進行每次查詢時
- 當你打開一個安全 Shell(SSH)連接到一個服務器來做一些任務時。 而不想為每個任務重新打開連接
- 如果你需要限制對某些變量或空間的訪問,你可以使用一個單例作為 作為這個變量的門(在 Go 中使用通道可以很好地實現)
- 如果你需要限制對某些空間的調用數量,你可以創建一個單例實例使得這種調用只在可接受的窗口中進行
單例模式還有跟多的用途,這里只是簡單的舉出一些。
單例模式例子:特殊的計數器
我們可以寫一個計數器,它的功能是用于保存它在程序執行期間被調用的次數。這個計數器的需要滿足的幾個要求:
- 當之前沒有創建過計數器
count
時,將創建一個新的計數器count = 0
- 如果已經創建了一個計數器,則返回此實例實際保存的
count
數 - 如果我們調用方法
AddOne
一次,計數count
必須增加 1
在這個場景下,我們需要有 3 個測試來堅持我們的單元測試。
第一個單元測試
與 Java 或 C++ 這種面向對象語言中不同,Go 實現單例模式沒有像靜態成員的東西(通過 static 修飾),但是可以通過包的范圍來提供一個類似的功能。
首先,我們要為單例對象編寫包的聲明:
package singleton type Singleton struct { count int } var instance *Singleton func init() { instance = &Singleton{} } func GetInstance() *Singleton { return nil } func (s *Singleton) AddOne() int { return 0 }
然后,我們通過編寫測試代碼來驗證我們聲明的函數:
package singleton import ( "testing" ) func TestGetInstance(t *testing.T) { count := GetInstance() if count == nil { t.Error("A new connection object must have been made") } expectedCounter := count currentCount := count.AddOne() if currentCount != 1 { t.Errorf("After calling for the first time to count, the count must be 1 but it is %d\n", currentCount) } count2 := GetInstance() if count2 != expectedCounter { t.Error("Singleton instances must be different") } currentCount = count2.AddOne() if currentCount != 2 { t.Errorf("After calling 'AddOne' using the second counter, the current count must be 2 but was %d\n", currentCount) } }
第一個測試是檢查是顯而易見,但在復雜的應用中,其重要性也不小。當我們要求獲得一個計數器的實例時,我們實際上需要得到一個結果。
我們把對象的創建委托給一個未知的包,而這個對象在創建或檢索對象時可能失敗。我們還將當前的計數器存儲在變量 expectedCounter
中,以便以后進行比較。即:
currentCount := count.AddOne() if currentCount != 1 { t.Errorf("After calling for the first time to count, the count must be 1 but it is %d\n", currentCount) }
運行上面的代碼:
$ go test -v -run=GetInstance . === RUN TestGetInstance singleton_test.go:12: A new connection object must have been made singleton_test.go:19: After calling for the first time to count, the count must be 1 but it is 0 singleton_test.go:31: After calling 'AddOne' using the second counter, the current count must be 2 but was 0 --- FAIL: TestGetInstance (0.00s) FAIL FAIL github.com/yuzhoustayhungry/GoDesignPattern/singleton 0.412s FAIL
單例模式實現
最后,我們必須實現單例模式。正如我們前面提到的,通常做法是寫一個靜態方法和實例來檢索單例模式實例。
在 Go 中,沒有 static
這個關鍵字,但是我們可以通過使用包的范圍來達到同樣的效果。
首先,我們創建一個結構體,其中包含我們想要保證的對象 在程序執行過程中成為單例的對象。
package singleton type Singleton struct { count int } var instance *Singleton func init() { instance = &Singleton{} } func GetInstance() *Singleton { if instance == nil { instance = new(Singleton) } return instance } func (s *Singleton) AddOne() int { s.count++ return s.count }
我們來分析一下這段代碼的差別,在 Java 或 C++ 語言中,變量實例會在程序開始時被初始化為 NULL
。 但在 Go 中,你可以將結構的指針初始化為 nil
,但不能將一個結構初始化為 nil
(相當于其他語言的 NULL
)。
所以 var instance *singleton*
這一語句定義了一個指向結構的指針為 nil
,而變量稱為 instance
。
我們創建了一個 GetInstance
方法,檢查實例是否已經被初始化(instance == nil
),并在已經分配的空間中創建一個實例 instance = new(singleton)
。
Addone()
方法將獲取變量實例的計數,并逐個加 1,然后返回當前計數器的值。
再一次運行單元測試代碼:
$ go test -v -run=GetInstance . === RUN TestGetInstance --- PASS: TestGetInstance (0.00s) PASS ok github.com/yuzhoustayhungry/GoDesignPattern/singleton 0.297s
單例模式優缺點
優點:
- 你可以保證一個類只有一個實例。
- 你獲得了一個指向該實例的全局訪問節點。
- 僅在首次請求單例對象時對其進行初始化。
缺點:
- 違反了單一職責原則。?該模式同時解決了兩個問題。
- 單例模式可能掩蓋不良設計,?比如程序各組件之間相互了解過多等。
- 該模式在多線程環境下需要進行特殊處理,?避免多個線程多次創建單例對象。
- 單例的客戶端代碼單元測試可能會比較困難,?因為許多測試框架以基于繼承的方式創建模擬對象。?由于單例類的構造函數是私有的,?而且絕大部分語言無法重寫靜態方法,?所以你需要想出仔細考慮模擬單例的方法。?要么干脆不編寫測試代碼,?或者不使用單例模式。
原文鏈接:https://juejin.cn/post/7156185032195833863
相關推薦
- 2022-11-15 超詳細解析C++實現歸并排序算法_C 語言
- 2021-12-10 Oracle數據庫備份還原詳解_oracle
- 2021-11-18 C/C++?Qt?TableDelegate?自定義代理組件使用詳解_C 語言
- 2022-07-30 Qt實現不規則窗體
- 2022-06-24 深度解析Python線程和進程_python
- 2022-06-22 Git配置用戶簽名方式的拓展示例實現全面講解_其它綜合
- 2022-12-22 C++模擬實現STL容器vector的示例代碼_C 語言
- 2022-07-13 4:thingsboard的實體與關系
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支