DesignPattern: Null Object, Singleton
Null object pattern/空物件模式
我認為空物件模式是讓程式碼保持乾淨,並且語意明確的一種方法。為了好debug,在寫C時我已養成加上要檢查函數參數中的指標是否為NULL,不然可能程式執行到一半就會crash掉,然後找半天都不知道錯誤在哪裡;不過在寫Java時,這習慣就會變成不檢測,直接讓程式crash反正最後也會有jvm會幫我印出錯誤位置點。
Christopher Okhravi教學影片
https://www.youtube.com/watch?v=rQ7BzfRz7OY
如何實現 Null Object Pattern ? (使用PHP展示)
http://oomusou.io/design-pattern/nullobject/
Null Object 模式並非出自設計模式一書,而是出現在重構的 Ch 9.7,教大家將null
值重構成null
物件,因為只要有null
值,就必須去if
判斷是否null
,甚至於去try catch
,這樣的 API 並不好用,而且只要忘記判斷就可能出錯。
Singleton pattern/單例模式
這個類別在整個世界中只會存在一個實體, 不論如何取得都是同一個實體。Christopher Okhravi教學影片
https://www.youtube.com/watch?v=hUE_j6q0LTQ
良葛格和RunNoob.com都有用java語言寫幾種範例
這個影片動態的展示了未保護的singleton在multithread狀態會有問題
https://www.youtube.com/watch?v=iyfqDV4wKAQ
1. 實作單例基本精神, 並具備multithread保護的能力
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2. 多方都在用的double-checked locking方法, 避免使用synchronized method降低執行效率
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
關於volatile關鍵字特性:淺談Java的volatile
Volatile修飾的成員變數在每次被線程訪問時,都強迫從共用記憶體中重讀該成員變數的值。而且,當成員變數發生變化時,強迫線程將變化值回寫到共用記憶體。這樣在任何時刻,兩個不同的線程總是看到某個成員變數的同一個值。
3. 沒有使用lock, 僅透過Java classloader在初始化class時只會使用單執行緒來完成的特性(個人覺得這個寫法也滿簡單的)
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
4. 最特別的是使用java的enum關鍵字來實作單例模式
dzone的這篇文章提供更多資訊關於用enum實作單例模式, 這種實作方式可以防止有人使用java反射機制呼叫私有建構子, 導致上面這些singlton實作都破功
public enum SingletonEnum {
INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
使用C++實作singlton可以參考
1. Stackoverflow的解答中, 在C++11可以用很簡單的寫法完成singleton
https://stackoverflow.com/questions/12248747/singleton-with-multithreads
static Singleton* Singleton::getSingletonInstance()
{
static Singleton instance;
return &instance;
}
不過這裡要注意最好將複製建構子跟賦值運算子都改成private避免C++的自動拷貝行為 private:
Singleton(Singleton const&) // copy constructor
void operator=(Singleton cons&); // assign operator
2. fcamel在以 double-checked locking 為例,了解 memory barrier 的作用以及thread 之間何時會同步資料 中引述了關於memory model說明, 為了防止CPU亂序執行(out-of-order execution)需要memory barrier的幫助
初始版本
這作法相當於java用synchrnized method
Singleton* s_instance;
Singleton* Singleton::GetInstance()
{
ScopeLock lock;
if (s_instance == nullptr)
s_instance = new Singleton();
return s_instance;
}
進階版本
fcamel引用自Preshing on Programming:Double-Checked Locking is Fixed In C++11
std::atomic<Singleton*> Singleton::s_instance;
std::mutex Singleton::s_mutex;
Singleton* Singleton::GetInstance() {
Singleton* tmp = s_instance.load();
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(s_mutex);
tmp = s_instance.load();
if (tmp == nullptr) {
tmp = new Singleton();
s_instance.store(tmp);
}
}
return tmp;
}
另外, 在(learn&think):浅谈设计模式六: 单例模式(Singleton)中說明, 為何不能在C++中使用volatile關鍵字來完成double-checked lokcking pattern的單例實作。
---
良葛格(林信良) Design Pattern(繁中)
https://openhome.cc/Gossip/DesignPattern/index.html
RunNoob.com(簡中)
http://www.runoob.com/design-pattern/design-pattern-tutorial.html
留言