运用共享技术有效地支持大量细粒度对象的复用。系统只使用少许的对象,而这些对象都很类似,状态变化很小,能够实现对象的屡次复用。因为享元模式要求可以共享的对象必须是细粒度对象,所以它又称为轻量级模式,它是一种对象结构型模式。
我我的理解为,把基本不变、并能够屡次使用的细颗粒度(小)对象,加载到内存中保存起来,而后对外提供业务。业务是指组合这些细颗粒度对象提供更丰富的数据,每次使用时没必要再加载一次,直接从内存中读取。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),中间件也提供了不少查询方式,因此这个模式渐渐用的不算多了。
以上就是我对享元模式的一些理解,有不足之处请你们指出,谢谢。