JAVA基礎(四)枚舉(enum)和常量定義,工廠類使用對比

Java極客  |  做者  /  鏗然一葉
這是Java極客的第 64 篇原創文章

相關閱讀:java

JAVA基礎(一)簡單、透徹理解內部類和靜態內部類
JAVA基礎(二)內存優化-使用Java引用作緩存
JAVA基礎(三)ClassLoader實現熱加載
JAVA基礎(五)函數式接口-複用,解耦之利刃
JAVA編程思想(一)經過依賴注入增長擴展性
JAVA編程思想(二)如何面向接口編程
JAVA編程思想(三)去掉彆扭的if,自注冊策略模式優雅知足開閉原則
JAVA編程思想(四)Builder模式經典範式以及和工廠模式如何選?
HikariPool源碼(二)設計思想借鑑
人在職場(一)IT大廠生存法則編程


1. 枚舉的用途

枚舉能夠用來定義常量,也能夠看成工廠類使用,其相比常量定義,定義能夠更集中;相比工廠類,表達is A(某一種類型)的語義更強。緩存

2. 常量定義例子

2.1. 常量定義

class Constants {
    // 常量定義方式通常經過相同前綴來分類,只要保證定義在同一個代碼段就沒問題,若是分散到多個代碼段,找起來就挺費勁
    public static final int SERV_TYPE_CAR = 1;
    public static final int SERV_TYPE_TV = 2;
    public static final int SERV_TYPE_MOBILE = 3;

    // 經過枚舉定義的方式更加集中,不可能會分散到多個代碼段
    public enum SERV_TYPE {
        CAR, TV, MOBILE
    }
}
複製代碼

2.2. 使用方式

// 使用常量
    public static void dox(int servType) {
        switch (servType) {
            case Constants.SERV_TYPE_CAR:
                // do something
                break;
            case Constants.SERV_TYPE_MOBILE:
                // do something
                break;
            case Constants.SERV_TYPE_TV:
                // do something
                break;
            default:
                break;
        }
    }

    // 使用枚舉
    public static void doy(Constants.SERV_TYPE servType) {
        switch (servType) {
            case CAR:
                // do something
                break;
            case TV:
                // do something
                break;
            case MOBILE:
                // do something
                break;
            default:
                break;
        }
    }
複製代碼

可見,在使用上無多大區別,枚舉的好處是在定義時更加集中,好維護,同時還限定了入參的取值範圍,使得更不容易發生參數輸入錯誤的狀況。ide

3. 工廠類用法例子

枚舉充當工廠類的用法在不少開源項目中均可以看到,其做用和工廠類同樣。先看下例子的類結構:函數

職責
CacheType 緩存類型,枚舉類,經過它來獲取對應的緩存類實例
BasicCache 緩存類接口,對外暴露
ConcurrentMapCache ConcurrentMap實現的緩存,能夠不對外暴露
LinkedHashMapCache LinkedHashMap實現的緩存,能夠不對外暴露

3.1. 代碼

3.1.1. 緩存接口

public interface BasicCache<K, V> {
    V get(K key);

    void put(K key, V value);
}
複製代碼

3.1.2. 緩存實現類

// 注:沒有public修飾,只在包內訪問,屏蔽了可見性,對外只暴露BasicCache
final class LinkedHashMapCache<K, V> implements BasicCache<K, V> {
    private final Map<K, V> map;

    public LinkedHashMapCache(int maximumSize, boolean accessOrder) {
        map = new BoundedLinkedHashMap<>(maximumSize, accessOrder);
    }

    @Override
    public V get(K key) {
        synchronized (map) {
            return map.get(key);
        }
    }

    @Override
    public void put(K key, V value) {
        synchronized (map) {
            map.put(key, value);
        }
    }

    static final class BoundedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
        private static final long serialVersionUID = 1L;
        private final int maximumSize;

        public BoundedLinkedHashMap(int maximumSize, boolean accessOrder) {
            super(maximumSize, 0.75f, accessOrder);
            this.maximumSize = maximumSize;
        }

        @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maximumSize;
        }
    }
}

final class ConcurrentMapCache<K, V> implements BasicCache<K, V> {
    private final ConcurrentMap<K, V> map;

    public ConcurrentMapCache(ConcurrentMap<K, V> map) {
        this.map = requireNonNull(map);
    }

    @Override
    public V get(K key) {
        return map.get(key);
    }

    @Override
    public void put(K key, V value) {
        map.put(key, value);
    }
}
複製代碼

3.1.3. 緩存類型

public enum CacheType {
    ConcurrentHashMap {
        @Override public <K, V> BasicCache<K, V> create(int maximumSize) {
            return new ConcurrentMapCache<>(new ConcurrentHashMap<>(maximumSize));
        }
    },
    LinkedHashMap {
        @Override public <K, V> BasicCache<K, V> create(int maximumSize) {
            return new LinkedHashMapCache<>(maximumSize, true);
        }
    };

    public abstract <K, V> BasicCache<K, V> create(int maximumSize);
}
複製代碼

3.1.4. 枚舉類使用

// 使用枚舉,可充當工廠類做用
BasicCache<String, String> enumConcurrentHash = CacheType.ConcurrentHashMap.create(2);
BasicCache<String, String> enumLinkedHash = CacheType.LinkedHashMap.create(2);
複製代碼

能夠看到,此時枚舉類充當了工廠類的做用。post

3.1.5. 對比直接實例化

若是ConcurrentMapCache和LinkedHashMapCache定義爲public的,那麼能夠直接實例化,以下:優化

BasicCache<String, String> concurrentMapCache = new ConcurrentMapCache<>(new ConcurrentHashMap<>(2));
BasicCache<String, String> linkedHashMapCache = new LinkedHashMapCache<>(2, true);
複製代碼

既然能夠直接實例化,爲何還須要經過枚舉來建立實例? 其主要目的和工廠類同樣:屏蔽建立類實例的邏輯,外部直接使用接口就好ui

3.1.6. 對比工廠類用法

// 工廠類
public final class CacheFactory {
    public static BasicCache createConcurrentMapCache(int maximumSize) {
        return new ConcurrentMapCache<>(new ConcurrentHashMap<>(maximumSize));
    }

    public static BasicCache createLinkedHashMapCache(int maximumSize) {
        return new LinkedHashMapCache<>(maximumSize, true);
    }
}

// 使用
BasicCache<String, String> concurrentHashCache = CacheFactory.createConcurrentMapCache(2);
BasicCache<String, String> linkedHashCache = CacheFactory.createLinkedHashMapCache(2);
複製代碼

從以上例子類看,枚舉和工廠類用法並無多大區別.this

4. 總結

  1. 在做爲常量定義使用時,枚舉和常量定義差異不大,只是定義更加集中,方便維護
  2. 在做爲建立工廠使用時,枚舉和工廠類沒啥差異,只是業務語義稍有不一樣,枚舉的is A語義更強,能夠先表達is A而後再建立具體類實例,而工廠類是一步到位建立具體類實例。
  3. 工廠和直接new實例的區別是,工廠能夠屏蔽建立實例的細節,只暴露實例的接口,尤爲在建立複雜實例時,工廠的優點比經過new來建立類實例大得多。
  4. 可見,以上差異不是很大,具體使用時不須要太糾結,不必爭個孰優孰劣。

end.spa


<--閱過留痕,左邊點贊!

相關文章
相關標籤/搜索