Singleton在軟體設計上屬於很常見, 也很易於使用的一個樣板, 主要設計用於開發者對某個物件限制其只能產生一個的時候使用, 以下對其設計思維以及實作方式做說明.
.Thinking in Singleton Pattern
Singleton正如其名, 主要是為了確保目標物件只產生一個而設計, 常見的Design Patterns中有很多其他的樣板常使用Singleton來實作, 像是Abstract Factory, State Pattern等等.
我們在什麼需求下會使用到Singleton, 在這邊舉一個簡單的例子來做說明, 假設我們要設計一個遊戲, 在這個遊戲裡面有各種各樣的場景, 為了不要讓程式架構中場景的處理太過混亂, 我希望設計一個場景管理器, 把這些場景的各種功能, 像是產生場景, 釋放場景等等都交由這個場景管理器處理, 那麼理所當然的, 我必須確認整個程式中所有的場景都交由這"一個"管理器做處理, 為了保證不讓程式中其他地方不小心產生額外的場景處理器, 此時就可以使用Singleton樣板.
.Implement
為了確保只產生一個實體物件, Singleton的實作會在class中利用static成員變數的方式來確保產生唯一的Singleton物件, 並實作一個static函式提供用來取得這個Singleton物件, 架構並不複雜, 如下圖所示.
Singleton架構
那麼, 實際以程式碼實作層面來看上圖的話, 需要注意幾個重點,以下列出注意點以及程式實做.
- 為了不讓外部有權限產生Singleton物件, 其建構子會定為private
- 為了不讓外部有權限對Singleton裡面存取的物件做更動, 用來儲存的static成員變數instance也會定為private
- 外部要使用的話統一透過GetInstance函式取來用
程式碼實做
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } //other method... public void test() { System.out.println("Hello Singleton!"); }; } class App { public static void main(String[] args) { Singleton.getInstance().test(); } };
以上執行結果會印出"Hello Singleton!", 不過以上的架構只是最基本的Singleton, 有開發過多執行序程式的人就會發現, 假設有多個執行序幾乎同時在程式第一次呼叫getInstance時, 在if判斷null與new產生物件之間, 同時另一個執行序也有可能跑到if判斷, 結果因為還沒new, 判斷是null, 也進去new, 結果產生了多個Singleton導致問題, 針對此問題有很多人提出討論以及解決辦法, 以下簡略做說明.
.Extended Reading
Double-checked locking and the Singleton pattern
- http://www.ibm.com/developerworks/java/library/j-dcl/index.html
如上列網址所探討, 針對在多執行序下Singleton的架構, 有人提出各種利用synchronized再多加一層處理的方式, 防止另一個執行序在當時同時做處理,如下所示
利用synchronized處理Singleton
public static synchronized Singleton getInstance() { if (instance == null) //1 instance = new Singleton(); //2 return instance; //3 } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { instance = new Singleton(); } } return instance; } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 if (instance == null) //2 instance = new Singleton(); //3 } } return instance; }
不過利用synchronized多多少少會影響到程式本身執行的效率, 所以也有人提出直接在class產生時就把物件建好, 一勞永逸, 不需要在GetInstance時還要判斷, 程式如下所示
預先建好Singleton物件
class Singleton { private Vector v; private boolean inUse; private static Singleton instance = new Singleton(); private Singleton() { v = new Vector(); inUse = true; //... } public static Singleton getInstance() { return instance; } }
以這幾種方式來看, 如果class本身沒有很複雜, 我個人是比較建議直接在class產生就建好, 畢竟synchronized影響的效率也不算小,又是多執行序一堆在取, 影響又更大了, 所以以我而言應該就比較偏向採用先建好的方式來實作, 另外, 在Java上實作可能要注意一些其他問題, 如下面網址, 有空的人可以鑽研一下.....
The "Double-Checked Locking is Broken" Declaration
- http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
沒有留言:
張貼留言