1. 程式人生 > >GOF設計模式之單例模式

GOF設計模式之單例模式

模塊 ive sys 其他 故事 time() 設置 nta oge

定義

單例模式(Singleton Pattern)的定義如下:Ensure a class only has one instance, and provide a global point of access to it(確保某一個類只有一個實例,並且提供一個全局訪問點來訪問此實例)。在JVM應用中,單例模式表現為一個類在JVM中只有一個實例。一個相對合理的類圖如下:

技術分享圖片

使用場景

  • 1、系統中需要一個共享的訪問點或者共享數據,例如Web請求計數器。
  • 2、創建一個對象需要消耗的資源過多,例如IO、數據庫資管等。
  • 3、需要定義大量的靜態常量或者靜態方法(例如工具類),可以考慮采用單例模式。

在JVM中典型的真實例子如下:

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

適用性

單例模式的優勢

  • 采用單例模式的類能確保在一個應用中只有一個實例,減少了內存消耗以及創建或者銷毀類實例時候的性能損耗。
  • 可以避免對資源的多重占用。
  • 可以設置應用的全局訪問點,優化和共享資源訪問。

單例模式的劣勢

  • 單例模式一般沒有接口或者基類,擴展困難,擴展必須修改類代碼。
  • 緊密耦合的代碼,對測試不利,簡單來說就是不能Mock掉。
  • 單例模式違反單一責任原則,因為它既要保持"單例"又要顧及業務邏輯。

實現方式

懶漢方式

懶漢方式的關鍵字在於"懶",也就是懶加載(Lazy Load),一個很常見的使用方式就是雙重檢查鎖定(Double-Check Locking):

public class Singleton {

    private static volatile Singleton INSTANCE = null;
    
    private Singleton(){

    }

    public static Singleton getInstance(){
        if (null == INSTANCE){
            synchronized (Singleton.class){
                if (null == INSTANCE){
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

餓漢方式

餓漢方式的實現相對簡單:

public class Singleton {

    private static volatile Singleton INSTANCE = new Singleton();

    private Singleton(){

    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

靜態內部類方式

使用靜態內部類方式的好處是既可以實現延遲加載,又可以保證線程安全,它的實現如下:

public class Singleton {

    private Singleton(){

    }
    
    private static class InterClassHolder{
        private final static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return InterClassHolder.INSTANCE;
    }
}

最佳實踐-單元素枚舉方式

《Effective Java》第2版中指出:單元素枚舉類型是實現單例的最佳方式。這是因為,前面說到的三種實現方式都可以通過反射改變類的行為,但是枚舉類型可以避免這個問題。建議在所有需要使用到單例模式的情況下直接使用單元素枚舉方式實現單例:

public enum Singleton {

    INSTANCE;

    public void sayHello() {

    }
}

使用方式:Singleton.INSTANCE.sayHallo()

故事

Doge是公司裏一個核心項目的開發組長,手下有十多個組員分別負責開發項目的不同模塊。

技術分享圖片

技術分享圖片

Doge展示了一個日期工具類和它的使用情況:

public class DateUtils {

    public static String format(LocalDateTime target,String pattern){
        return DateTimeFormatter.ofPattern(pattern).format(target);
    }

    public LocalDateTime parse(String target,String pattern){
        return LocalDateTime.parse(target, DateTimeFormatter.ofPattern(pattern));
    }
}

//調用情況
DateUtils.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
LocalDateTime target = new DateUtils().parse("2018-7-29 12:12:30","yyyy-MM-dd HH:mm:ss");

技術分享圖片

小黑貼了一下重寫的工具類:

public enum DateUtils {

    SINGLETON;

    private static final Map<String, DateTimeFormatter> FORMATTER_CACHE = new HashMap<>();

    public String format(LocalDateTime target, String pattern) {
        return getOrCreateFormatter(pattern).format(target);
    }

    public LocalDateTime parse(String target, String pattern) {
        return LocalDateTime.parse(target, getOrCreateFormatter(pattern));
    }


    private DateTimeFormatter getOrCreateFormatter(String pattern) {
        DateTimeFormatter formatter;
        if (FORMATTER_CACHE.containsKey(pattern)) {
            formatter = FORMATTER_CACHE.get(pattern);
        } else {
            formatter = DateTimeFormatter.ofPattern(pattern);
            FORMATTER_CACHE.put(pattern, formatter);
        }
        return formatter;
    }
}

//調用
DateUtils.SINGLETON.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
LocalDateTime target = DateUtils.SINGLETON.parse("2018-7-29 12:12:30", "yyyy-MM-dd HH:mm:ss");

技術分享圖片

Doge拷貝了小黑的工具類代碼,並且仿照這個類的邏輯完成了其他工具類的代碼重構。

(本文完)

GOF設計模式之單例模式