前面講了這麼多,還沒有實例登場。
這次,書中提到的範例是巧克力工廠─ 工廠的運作依賴著對巧克力鍋爐狀態的偵測,依據不同的狀態執行不同的動作,避免災難發生!它的運作方式如下:
class ChocolateBoiler
{
private:
bool empty;
bool boiled;
public:
ChocolateBoiler()
{
empty = true;
boiled = false;
}
void fill()
{
if(isEmpty())
{
empty = false;
boiled = false;
cout<<"fill!"<<endl;
}
}
bool isEmpty()
{
return empty;
}
bool isBoiled()
{
return boiled;
}
};
而因為只有一個鍋爐,我們必須避免讓兩個ChocolateBoiler
實例被創造出來,因此加入前面經典單例模式的寫法:
class ChocolateBoiler
{
private:
static ChocolateBoiler *uniqueInstance;
ChocolateBoiler()
{
empty = true;
boiled = false;
};
bool empty;
bool boiled;
public:
static ChocolateBoiler *getInstance()
{
if(!uniqueInstance)
{
uniqueInstance = new ChocolateBoiler();
}
return uniqueInstance;
}
void fill()
{
if(isEmpty())
{
empty = false;
boiled = false;
cout<<"fill!"<<endl;
}
}
bool isEmpty()
{
return empty;
}
bool isBoiled()
{
return boiled;
}
};
前面有提到這個寫法可能有潛在的問題,究竟為何?
當現在有兩個執行序時,分別執行以下這段程式:
ChocolateBoiler * boiler = ChocolateBoiler::getInstance();
boiler->fill();
是否有可能有兩個實例同時被產生的情形呢?
例如在以下的情況下交錯...
那要如何解決?
有幾種方式可以處理這種情形:
getInstance()
改為同步方法。例如以下:class Singleton
{
private:
static Singleton *uniqueInstance;
Singleton(){};
static mutex uniqueInstance_mtx;
public:
static Singleton *getInstance()
{
lock_guard<mutex> lock(uniqueInstance_mtx);
if(!uniqueInstance)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
};
getInstance()
不常被使用,或不會太嚴重影響程式效能,就可以採用此方法。2.使用 急性(eager) 而非 惰性(lazy) 創建實例。例如以下:
class Singleton
{
private:
static Singleton *uniqueInstance;
Singleton(){};
public:
static Singleton *getInstance()
{
return uniqueInstance;
}
};
Singleton * Singleton::uniqueInstance = new Singleton();
3.使用 "雙重檢查鎖(double checked locking)" ,在getInstance()
中減少同步化。例如以下:
class Singleton
{
private:
volatile static Singleton *uniqueInstance;
Singleton(){};
static mutex uniqueInstance_mtx;
public:
volatile static Singleton *getInstance()
{
if(!uniqueInstance)
{
lock_guard<mutex> lock(uniqueInstance_mtx);
if(!uniqueInstance)
{
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
};
除此之外,還有java可直接使用enum來達到singleton的效果,且不會有執行序等等的副作用,可參考這邊的實作。
結束了輕鬆的這一回合,終於來到鐵人賽的一半!目前已經涵蓋了六章,歷經六個原則與七個模式定義,不妨回頭整理複習一下這些模式與他們的架構圖,以及背後那些原則們,再繼續看下去各式各樣的模式們。