《一天一模式》— 享元模式

1、享元模式的概念

運用共享技術有效地支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種對象結構型模式。

2、何時使用享元模式

我我的理解爲,把基本不變、並能夠屢次使用的細顆粒度(小)對象,加載到內存中保存起來,而後對外提供業務。業務是指組合這些細顆粒度對象提供更豐富的數據,每次使用時沒必要再加載一次,直接從內存中讀取。java

享元模式是一個考慮系統性能的設計模式,經過使用享元模式能夠節約內存空間,提升系統的性能。數據庫

3、如何使用享元模式

3.1 實現方式

仍是以一個需求進行說明:假設汽車有不少故障的解決信息FAQ信息,當發生某種故障時,找出對應的幾條FAQ信息,把他們返回給用戶。設計模式

看一下類圖和代碼:緩存

// 模擬故障數據庫
// 假設是在磁盤系統保存這些數據
public class DTCDataBase {

    private static Map<String, String> info = new HashMap<>();

    static {
        info.put("1", "故障A");
        info.put("2", "故障B");
        info.put("3", "故障C");
        info.put("4", "故障D");
        info.put("5", "故障E");
        info.put("6", "故障F");
        info.put("7", "故障G");
    }

    private DTCDataBase() {
    }

    public static String getDTC(String code) {
        System.out.println("DTCDataBase: 從數據庫中檢索信息。");
        return info.get(code);
    }

}
// 故障信息的享元對象
public class DTCInfo {

    private String info;

    public DTCInfo(String code) {
        // 建立對象時,去數據庫中查詢故障信息
        this.info = DTCDataBase.getDTC(code);
    }

    public void print() {
        System.out.println("故障信息:" + info);
    }

}
// 故障信息的享元工廠
public class DTCInfoFactory {

    // 建立一個享元對象的內存池
    private Map<String, DTCInfo> pool = new HashMap<>();

    // 開始單例模式
    private static DTCInfoFactory singleton = new DTCInfoFactory();

    private DTCInfoFactory() {
    }

    public static DTCInfoFactory getInstance() {
        return singleton;
    }
    // 結束單例模式

    // 獲取享元對象
    public synchronized DTCInfo getDTCInfo(String code) {
        // 先從池中檢索,若是有就返回緩存的享元對象
        // 若是沒有就去數據庫中檢索,檢索後的結果在放入池中
        DTCInfo dtc = pool.get(code);
        if (dtc == null) {
            dtc = new DTCInfo(code);
            pool.put(code, dtc);
        }
        return dtc;
    }

}
// FAQ,用於測試
public class FAQ {

    // 模擬引擎故障,FAQ返回故障A,C
    public void engineOn() {
        DTCInfoFactory dtcInfoFactory = DTCInfoFactory.getInstance();
        dtcInfoFactory.getDTCInfo("1").print();
        dtcInfoFactory.getDTCInfo("3").print();
    }

    // 模擬熄火故障,FAQ返回故障B,D,E
    public void engineOff() {
        DTCInfoFactory dtcInfoFactory = DTCInfoFactory.getInstance();
        dtcInfoFactory.getDTCInfo("2").print();
        dtcInfoFactory.getDTCInfo("4").print();
        dtcInfoFactory.getDTCInfo("5").print();
    }

}
public class Client {

    public static void main(String[] args) {
        // 第一次會從數據庫中檢索;
        System.out.println("第一次,會從數據庫中檢索。");
        test();
        System.out.println();
        System.out.println("第二次就會從享元工廠中檢索。");
        // 第二次就會從享元工廠中檢索;
        test();
    }

    private static void test() {
        FAQ faq = new FAQ();
        // 點火故障,FAQ會給出的故障信息列表
        System.out.println("【點火故障,FAQ會給出的故障信息列表】");
        faq.engineOn();
        System.out.println("-------------------------");
        // 熄火故障,FAQ會給出的故障信息列表
        System.out.println("【熄火故障,FAQ會給出的故障信息列表】");
        faq.engineOff();
    }

}

輸出:bash

第一次,會從數據庫中檢索。
【點火故障,FAQ會給出的故障信息列表】
DTCDataBase: 從數據庫中檢索信息。
故障信息:故障A
DTCDataBase: 從數據庫中檢索信息。
故障信息:故障C
-------------------------
【熄火故障,FAQ會給出的故障信息列表】
DTCDataBase: 從數據庫中檢索信息。
故障信息:故障B
DTCDataBase: 從數據庫中檢索信息。
故障信息:故障D
DTCDataBase: 從數據庫中檢索信息。
故障信息:故障E

第二次就會從享元工廠中檢索。
【點火故障,FAQ會給出的故障信息列表】
故障信息:故障A
故障信息:故障C
-------------------------
【熄火故障,FAQ會給出的故障信息列表】
故障信息:故障B
故障信息:故障D
故障信息:故障E

代碼總共分爲五個類:架構

  1. DTCDataBase:模擬數據庫的,沒什麼重點,僞裝是從磁盤讀取,性能不高;
  2. DTCInfo:享元對象,是概念中細顆粒度(小)的對象,保存一個故障信息,會被緩存;
  3. DTCInfoFactory:享元工廠,屬於核心類,也沒有特別難理解的,在工廠中維護一個內存池,保存從數據庫加載的享元對象;
  4. FAQ:用於實現需求,模擬不一樣的故障須要給車主返回相關的故障列表;
  5. Client:啓動類,一樣的業務作兩邊,看看是否第一次從數據庫檢索,第二次從內存檢索;

3.2 享元模式的好處

節省空間: 若是享元模式用於緩存比較大的對象,只new一個後便可,不會有頻繁new致使空間不足的問題發生;分佈式

性能提升: 只有第一次使用對象,會從資源中加載,後續的對象使用直接從享元模式提供的工廠中獲取便可;性能

3.3 思考與單例模式的不一樣

個人理解是,單例模式保存了一個對象在內存中只有一份測試

享元模式是確保一堆對象在其池中只有一份,而且根據業務需求,對外提供不一樣的對象列表。這裏要提到兩個概念:Intrinsic與Extrinsic。this

Intrinsic的意思是固有的、本質的,意思是什麼狀況下都不會改變的對象,上面的代碼示例中的DTCInfo就是Intrinsic,因此它適合被緩存。

Extrinsic的意思是外在的、非本質的,意思是會發生變化的對象,好比engineOn返回A,C故障,engineOff返回B,D,E故障,其餘狀況返回其餘的故障組合,這個故障組合就是Extrinsic,它不能被緩存,由於變化很頻繁。

4、總結

享元對象雖然結構上覆雜了一點,有單例、簡單工廠等等模式內嵌其中,可是從功能上來說很簡單,把不會改變的東西緩存起來,並根據不一樣業務作不一樣的返回,起到了節省空間提升性能的功效。

這個模式跟備忘錄模式的現狀有些相同。

  • 首先,要確保在內存中的對象不會被垃圾回收;
  • 其次,在分佈式部署的系統架構下,使用JVM內存則行不通;

現有的緩存中間件,已經逐漸能夠勝任這個模式了(例如Redis),中間件也提供了不少查詢方式,因此這個模式漸漸用的不算多了。

以上就是我對享元模式的一些理解,有不足之處請你們指出,謝謝。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息