Effective Java 讀書筆記: Item 3 - singleton 實作技巧

記錄讀書心得, 內容不一定和書上一致, 有些是我自己的看法。

書中先提到使用 singleton 的缺點: 會難以測試程式。畢竟, 使用 singleton 的必要條件是設定 constructor 的存取級別為 private。若要減輕這個問題, 可幫 singleton 加上 interface, 而不是直接用 singleton 的 class 來使用它的 methods。不過我想沒什麼人會想這麼做, 要寫兩套 signature, 挺麻煩的。

查了一下相關資訊, EasyMock 有提到它無法 mock private 或 final method (見 官網文件的 "Class Mocking Limitations" ), 看來 EasyMock 是用繼承的方式 mock class, 所以有此限制吧。vgod 提到 PowerMock 可 mock private, final, static methods, 官網提到它的作法是換掉 class loader, 有機會再來試試。至少知道還是有辦法測試 singleton, 只是可用手段較少, 或許也有一些其它副作用吧。

關於 singleton 更多的弊端, 可參見 Miško Hevery 的文章:
我還需要不少時間配合實例嘗試和思考, 才能明白使用 singleton 或替代方案的 trade-off。

在 java 5.0 開始, 用 enum 實作 singleton 又好寫又好用, 只要這麼寫即可:
public enum MyClass {
    INSTANCE;
    public void someOperation() { .. }
}
enum 的語法可直接做出「singleton」, Making the Most of Java 5.0: Enum Tricks 提供滿不錯的例子介紹使用 enum 的技巧, 像是各別定義各個物件的方法。

至於 java 5.0 前, 實作 singleton 有兩種選擇, 兩者都需要 private constructor, 差別在於取得 singleton 的方式:
  • 用 public static final MyClass INSTANCE = new MyClass()
  • 類似前者, 但改設 INSTANCE 的存取級別為 private, 改用 public static MyClass getInstance() 取得物件
後者的好處是較有彈性, 必要時可放棄 singleton 改為一般用法而不需改所有的 caller, 或是改成「每個 thread 有一個 singleton」。此外, 不用擔心 static method 的成本, 近代 JVM 會 inline static method call。但作者接著說這些好處不太顯眼, 直接用 static member field 較簡單, 語意較明確, 看到 static final 就知道是 singleton。

用 class 實作 singleton 時要另外注意 serialization, 記得寫 readResolve() 避免在 deserialization 時產生第二個 object。所以整體來看, 作者認為使用 enum 並只放一個元素 (INSTANCE) 是比較明智的選擇。

但是, 使用 enum 也會更難 mock。EasyMock 不行, 而 PowerMock 似乎可以。還有, 通常用 enum 時會看作 constant 外加一些輔助函式, 不會有太複雜的 state。但若用 enum 實作 singleton, 很可能會有複雜的初始化, 或需要存取外部資源。這樣測試時就必須 mock enum 了。

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

熟悉系統工具好處多多

virtualbox 使用 USB 裝置