這篇我們學習的是單例模式,相信很多朋友都或多或少使用過這個模式。很多設計模式的入門,都把單例模式作為第一個的,但是因為我們是跟着書本學習,是以放在了第五個裡面。那麼,你使用過的單例模式是怎麼樣的呢?懶漢式?餓漢式?雙重校驗?靜态?
先來看下定義,單例模式(Singleton Pattern):用來建立獨一無二的,隻能有一個執行個體的對象的入場券。而且,單例模式的類圖是所有設計模式中最簡單的,事實上隻有一個類。但是,盡管從類的設計上來說簡單,實作上還是會遇見相當多的波折噢。
有些對象其實我們隻需要一個,比方說:線程池、緩存、對話框、處理偏好設定和系統資料庫對象、日志對象等。事實上,這些對象隻能以一個執行個體,如果制造出多個執行個體,就會導緻許多問題的産生,例如:程式的行為異常、資源使用過量,或者就是不一緻的結果。
我們先來看下經典的單例模式的實作代碼:
利用一個靜态變量來記錄Singleton類的唯一執行個體
把構造器聲明為私有化,隻有Singleton類才可以調用構造器
用getInstance()方法執行個體化對象,并傳回這個執行個體
再仔細看下getInstance()方法,這裡需要着重描述下。
uniqueInstance 擁有一個“執行個體”,而且是一個靜态變量
如果uniqueInstance是空的,表示還沒有建立執行個體
如果不存在,我們就利用私有的構造器産生一個Singleton執行個體并把它指派到uniqueInstance靜态變量中。請注意,如果我們不需要這個執行個體,他就永遠不會産生。這就是“延遲執行個體化”(lazy instaniaze)
如果uniqueInstance不是null,就表示之前已經建立過對象,我們就直接傳回
當執行到return語句,表示我們已經有執行個體,并将uniqueInstance當傳回值
如果沒有單例模式,這裡有一個代碼寫的很小心的例子,看完你肯定會感受到單例模式的重要性。

上圖中的公司有意識地防止不好的事情發生,對吧。但是,如果防不勝防,同僚存在兩個ChocolateBoiler執行個體,可能将發生很糟糕的事情。那麼,如果有過個ChocolateBoiler執行個體存在,可能發生什麼嚴重的事情呢?咋這個例子上,就是會産生資源的浪費,原料的溢出等等。
那麼,你能根據經典的單例模式,寫出這個巧克力工廠的單例模式嗎?我們晚點揭曉。
單例模式:確定一個類隻有一個執行個體,并提供一個全局通路點。
這定義一點兒都不讓人吃驚,但是讓我們更深入一點兒:
到底怎麼回事?我們正在把某個類設計成自己管理的一個單獨執行個體,同時也避免其他類再自行産生執行個體。要想取得單例執行個體,通過單例類是唯一的途徑
我們也提供對這個執行個體的全局通路點:當你需要執行個體時,向類查詢,他會傳回單個執行個體。前面的例子利用延遲執行個體化的方式建立單例,這種做法對資源敏感的對象特别重要。
那我們來看看單例的類圖:
你看吧,之前就說過,這個單例模式隻有一個類圖,是不是很簡單呢?仔細看看他吧。
但是,這些都隻是單線程模式下的單例模式,參考上面這個巧克力工廠,如果是多線程模式下的單例,那又會是什麼樣的呢?經典的單例模式,能確定你在單線程下不出問題,但是,我們想要讓人家效率更高,産量更大,勢必需要多線程?
那麼,請螢幕前的你,先好好想想,我們下次學習的時候,通過JVM原理,把這個煩惱給解決了。今天的學習就先到這裡啦。