寫到這時,我的軍旅生涯也不過過了三分之一,跟朋友說還有兩個半月,朋友告訴我說要說剩下,哈哈也是,總是要轉個念嘛。
今天要介紹的是設計模式中的單例模式,單例顧名思義就是「單一個」實例(Instance),只有一個實例,大家在寫程式的時候可能同一個 class 會重複建造,但是每一個都是新的個體,彼此是不相關的,單例模式則是保證這個類別只會有一個實體,不管從哪邊再呼叫這個 class,他們所指到的都會是同一個實例。
class Student{
public:
Student();
~Student();
private:
int number;
}
Student A;
Student B;
A.number = 10;
B.number = 20;
上面是一段很簡單的程式,有一個學生類別,分別創建了 A 學生、B 學生,因為我們沒有使用單例模式,這裡的 A、B 各自只到自己的實體,所以 A 的 number 就會是10,B 的 number 就會是20。
class Language{
public:
//此 function 必須是 static,才能在不宣告的情況下呼叫此 function
Language static getInstance(){
return instance;
}
private:
//把建構子放在 private 防止其他人創建此 class
Language();
//創建一個 static 的實例,並且在 getInstance 中回傳
Language static Language instance= new Language();
}
以上的程式,你只會有一個 Language 的 class,不能用建構子的方式創建新的,如果要取用 class,你可以用下面的方式:
Language lang = Langauge.getInstance();
看出來為什麼 getInstance 要用 static 了嗎,因為我們的建構子是 private,所以我們無法自己去創建這個 class,也就是說如果不是 static 的 function,我們都不能用。
以下會介紹幾種 Singleton 的使用方法。
class Language {
public:
Language static getInstance(){
if(instance == null)
instance = new Language();
return instance;
}
private:
Language();
static Language instance = null;
}
Lazy initialization 不會在一開始就把實例建好,而是等到第一次呼叫的時候才會建造,可以看到 getInstance 會判斷 instance 是不是 null,如果是的話代表是第一次呼叫,所以就要創建一個 language 的 instance。
此外,上面這種寫法只適合在單執行緒的情況,如果今天寫的程式會用到多執行緒(multithread),就有可能發生競爭(race)的情況,為了解決這個問題,我們必須使用同步(synchronized)。
在 C++ 中我們可以使用 mutex lock 去完成,C# 可以創建一個 static 的 object,使用 lock,在創建 instance 時 lock 住 function,在 java 中也有很多方式可以做,不過我自己都是用 synchronized 的方式去做。
C++
class Language{
public:
Language static getInstance(){
lock.lock();
if(instance == null)
instance = new Language();
//一定要 unlock 後再 return,不然資源會被 lock住
lock.unlock();
return instance;
}
private:
Language();
static Language instance = null;
}
C#
public sealed class Language{
private static volatile Languageinstance;
private static object lock= new object();
private Language() {}
public static LanguageInstance {
get {
if (instance == null) {
lock (lock) {
instance = new Language();
}
return instance;
}
}
}
}
Java
public class Language{
private static Language instance = null;
private Language(){}
synchronized static public Language getInstance() {
if (instance == null)
instance = new Language();
return instance;
}
}
上面這種方式是比較一般的做法,如果想要更進一步保證不會出錯的話,也有 Double-check 的模式,也就是也就是只有在第一次見例時才會進入同步。
Java(Double-check )
public class Language{
private static Language instance = null;
private Language(){}
public static Language getInstance() {
if (instance == null){
synchronized(Language.class){
if(instance == null) {
instance = new Language();
}
}
}
return instance;
}
上面的方法沒有說哪個好哪個不好,例如不使用同步的方法,速度比較快,但是捨棄了安全性,適合用在簡單、沒有多個執行緒的程式,所以寫程式沒有固定的寫法,都是經驗累積告訴你這邊要用什麼比較好。
參考資料:
https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/singleton.html
https://stackoverflow.com/questions/12316406/thread-safe-c-sharp-singleton-pattern