置頂 0%

Singleton Design Pattern

前言

Singleton Design Pattern 是一種設計的模式,目的是確保執行環境(JVM)內只會有一個物件實例,有點類似於Bean在Spring容器管理的Singleton作用域,意思一個IOC容器中只會有唯一的物件。它們都是希望可以避免系統資源的浪費,且確定這一個物件是無狀態的(Stateless),並能讓類別的腳色定義能更明確。常見Scope為Singleton的例子就是Spring boot專案裡的Service或是自己定義的Utility類別,我們並不需要每次都注入一個新的物件吧!

獨體設計模式建構

Singleton Design Pattern有幾個關鍵步驟來實踐

  1. 類別的建構子存取修飾子固定設為private
    • 目的為不予許其他外部類別使用new關鍵字來建立物件,也就保證了只有單一的物件
  2. 使用private static final來實體化這一物件
    • 因為物件只需要一份就好,所以在類別內使用private static final來宣告實體化這一物件,並表示該物件不可由外部類別直接存取(private),同時在JVM只會有一份(static),且該物件參考永遠不能指向其他參考(final)
  3. 提供公開的Getter方法來存取此物件
    • 最後需要一個公開的Getter方法來存取物件,慣例上會使用getInstance來返回此物件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SingletonPattern {
// Step 1
private SingletonPattern() {}
private String text = "Hello World";
// Step 2
private static final SingletonPattern singletonPattern = new SingletonPattern();
// Step 3
public static SingletonPattern getInstance() {
return singletonPattern;
}
public String getText() {
return text;
}
}

// ...
// SingletonPattern singleton = SingletonPattern.getInstance();

補充

在使用static必須要十分小心,加上static的變數會讓類別變成一個有狀態(State)的類別,如果在多執行續的情況下,常會造成結果跟預期的不相同,如下就是一個錯誤的例子,之前某舊專案使用到分頁,當時使用static來存放第幾筆資料和抓取數量,想說可以在Controller設定好資料,再EBO那邊直接再取就好,但經前輩指導後,這在多執行緒時就會有嚴重問題,這就是為何static要盡可能使用再無狀態的類別裡面!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Pager {
private static int start;
private static int limit;

private Pager() {}

private static final Pager pager = new Pager();

public static Pager getinstance() {
return pager;
}
public static void setPagerInfo(int first, int maxresult) {
start = first;//參數名稱要和static的成員變數不同,不然java預設會使用this.xxx
limit = maxresult;
}
public static int getStart() {
return start;
}
public static int getLimit() {
return limit;
}
}

前言提到的Bean作用域有分為下列幾種,最常使用的就是Singleton和Prototype,兩者差別就是前者適用無狀態(Stateless),後者適用有狀態的(Stateful)Bean

  • Singleton
    • 為預設的bean scope,整個IoC容器只會有唯一的實例
  • Prototype
    • 每次被調用都是建構新的實例
  • Request
    • 實例的scope為HTTP Request,同個request的實例才會相同 (Web環境才存在的scope)
  • Session
    • 實例的scope為HTTP Session,同個session的實例才會相同 (Web環境才存在的scope)