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

沒有留言:
張貼留言