運用共享技術有效地支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種對象結構型模式。
我我的理解爲,把基本不變、並能夠屢次使用的細顆粒度(小)對象,加載到內存中保存起來,而後對外提供業務。業務是指組合這些細顆粒度對象提供更豐富的數據,每次使用時沒必要再加載一次,直接從內存中讀取。java
享元模式是一個考慮系統性能的設計模式,經過使用享元模式能夠節約內存空間,提升系統的性能。數據庫
仍是以一個需求進行說明:假設汽車有不少故障的解決信息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
代碼總共分爲五個類:架構
節省空間: 若是享元模式用於緩存比較大的對象,只new一個後便可,不會有頻繁new致使空間不足的問題發生;分佈式
性能提升: 只有第一次使用對象,會從資源中加載,後續的對象使用直接從享元模式提供的工廠中獲取便可;性能
個人理解是,單例模式保存了一個對象在內存中只有一份。測試
享元模式是確保一堆對象在其池中只有一份,而且根據業務需求,對外提供不一樣的對象列表。這裏要提到兩個概念:Intrinsic與Extrinsic。this
Intrinsic的意思是固有的、本質的,意思是什麼狀況下都不會改變的對象,上面的代碼示例中的DTCInfo就是Intrinsic,因此它適合被緩存。
Extrinsic的意思是外在的、非本質的,意思是會發生變化的對象,好比engineOn返回A,C故障,engineOff返回B,D,E故障,其餘狀況返回其餘的故障組合,這個故障組合就是Extrinsic,它不能被緩存,由於變化很頻繁。
享元對象雖然結構上覆雜了一點,有單例、簡單工廠等等模式內嵌其中,可是從功能上來說很簡單,把不會改變的東西緩存起來,並根據不一樣業務作不一樣的返回,起到了節省空間和提升性能的功效。
這個模式跟備忘錄模式的現狀有些相同。
現有的緩存中間件,已經逐漸能夠勝任這個模式了(例如Redis),中間件也提供了不少查詢方式,因此這個模式漸漸用的不算多了。
以上就是我對享元模式的一些理解,有不足之處請你們指出,謝謝。