單例模式回顧
以前在做java的時候扁耐,經(jīng)常會用到設(shè)計模式潦蝇,如單例模式款熬、工廠模式、觀察者模式等攘乒。其實設(shè)計模式和語言無關(guān)贤牛,先簡單回顧下單例模式吧,單例模式是一種用在特定場景的設(shè)計模式则酝。比如殉簸,讀取程序的配置文件的時候就會用到單列模式。
想象一下沽讹,假入有個類的實例是來對配置文件進(jìn)行操作喂链,如果不用單例模式,系統(tǒng)中任何用到讀取配置文件的地方都將會創(chuàng)建一個對象妥泉,這得多么浪費內(nèi)存椭微。
實際上該對象只需要被實例化一次即可。單例模式的抽象表達(dá)就是:在程序中我們只需要某個類實例化一次即可盲链,保證一個類僅有一個實例蝇率,并提供一個獲取實例的方法。
單例模式的實現(xiàn)
單例模式有懶漢式和餓漢式刽沾。在用Go實現(xiàn)之前本慕,先看看Java的實現(xiàn)。
在java中不管是懶漢式還是餓漢式都會將構(gòu)造方法私有化侧漓。這點不用解釋锅尘,因為不需要通過外部來實例化對象,把創(chuàng)建對象的權(quán)限封鎖布蔗。
懶漢式
所謂懶漢式藤违,也就是在創(chuàng)建對象時比較懶嘛,先不著急創(chuàng)建對象纵揍,在需要的時候才創(chuàng)建對象顿乒。這里看下java的實現(xiàn),暫不考慮并發(fā)問題泽谨,并發(fā)加上synchronized即可璧榄。
1 public class Singleton {
2 private static Singleton single = null;
3 private Singleton(){
4 }
5 public static Singleton getSingle() {
6 if (single == null) {
7 single = new Singleton();
8 }
9 return single;
10 }
11}
那么上面的設(shè)計模式能否用Go語言實現(xiàn)呢特漩?答案是肯定的。Go語言沒有private這樣的權(quán)限控制骨杂,很簡單的是通過首字母大小寫來控制外部是否能夠訪問涂身。
1 type config struct {
2 }
3
4 var cfg *config
5 func getInstane() *config {
6 if cfg == nil {
7 cfg = new(Config)
8 return cfg
9 }
10 return cfg
11}
上面沒有考慮線程安全,我們可以自己加鎖保證安全搓蚪,也可以用Golang 中的sync.Once結(jié)構(gòu)體访得,該結(jié)構(gòu)體提供了一個Do方法,Do函數(shù)里面的函數(shù)只有在第一次才會被調(diào)用陕凹,該方法只會生成一個實例,且也是線程安全的鳄炉。
1 type config struct {
2 }
3
4 var cfg *config
5 var oSingle sync.Once
6
7 func getInstane() *config {
8 oSingle.Do(
9 func() {
10 cfg = new(config)
11 })
12 return cfg
13 }
餓漢式
餓漢模式和懶漢模式不同的只是在提供獲取實例的方法上杜耙。還是先來看下java的餓漢模式:
1 public class Singleton {
2 private static Singleton single = new Singleton();// 只會創(chuàng)建一次實例
3 private Singleton(){
4 }
5 public static Singleton getSingle() {
6 return single; // 直接返回
7 }
在go語言中,餓漢模式可以直接在init函數(shù)中初始化或者直接在全局變量中聲明拂盯。這區(qū)別于java中的變量必須是由static修飾佑女。因為static變量在類加載的時候進(jìn)行初始化。多個實例會共享這塊內(nèi)存空間谈竿。
關(guān)于為什么可以直接用全局變量团驱,下回再討論golang中的全局變量。
1 type cfg struct {
2 }
3 var cfg *config
4 func init() {
5 cfg = new(config)
6 }
7 // NewConfig 提供獲取實例的方法...
8 func NewConfig() *config {
9 return cfg
10 }
1 type config struct {
2 }
3 var cfg *config = new(config)
4 // NewConfig 提供獲取實例的方法...
5 func NewConfig() *config {
6 return cfg
7 }