iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 0
0
自我挑戰組

30天認識軟體設計及架構系列 第 8

Day 8 軟體設計及架構---設計模式 - 單例模式(Singleton)

寫到這時,我的軍旅生涯也不過過了三分之一,跟朋友說還有兩個半月,朋友告訴我說要說剩下,哈哈也是,總是要轉個念嘛。

今天要介紹的是設計模式中的單例模式,單例顧名思義就是「單一個」實例(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 的使用方法。

  1. Lazy initialization
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


上一篇
Day7 軟體設計及架構---設計模式介紹 Design Pattern
下一篇
Day 9 軟體設計及架構---設計模式 - 工廠模式(Factory)
系列文
30天認識軟體設計及架構10

尚未有邦友留言

立即登入留言