Java设计模式学习记录-享元模式 Java设计模式学习记
前言享元模式也是一种结构型模式,这篇是介绍结构型模式的最后一篇了(因为代理模式很早之前就已经写过了)。享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存损耗。 享元模式定义享元模式是指运用共享技术有效的支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。 举例咖啡问题,在一家咖啡店里有几种口味的咖啡,例如:拿铁、摩卡、卡布奇诺等等。最近这家店在搞促销活动,一中午能卖出几百杯咖啡,那么咖啡的口味就是一种共享的元素。下面用代码来实现一下这个例子。 定义订单接口 /** * 订单接口 */ public interface Order { //卖出咖啡 void sell(); } 具体的订单实现 class CoffeeOrder implements咖啡口味 public String flavor; CoffeeOrder(String flavor){ this.flavor = flavor; } @Override sell() { System.out.println("卖出了一份"+flavor+"的咖啡。"); } } 订单工厂类 import com.google.common.collect.Maps; java.util.Map; java.util.Objects; * 订单工厂类 class CoffeeOrderFactory { private static Map<String,Order> cof = Maps.newHashMap(); * 获得订单 * @param flavor 口味 * @return */ static Order getOrder(String flavor){ Order order = cof.get(flavor); if(Objects.isNull(order)){ order = new CoffeeOrder(flavor); cof.put(flavor,order); } return order; } * 获取最终创建的对象个数 * static int getSize(){ cof.size(); } } 测试类 org.assertj.core.util.Lists; java.util.List; * 测试买咖啡 MyTest { main(String[] args) { buyCoffee("拿铁"); buyCoffee("卡布奇诺"); buyCoffee("摩卡"); buyCoffee("拿铁"); buyCoffee("拿铁"); buyCoffee("卡布奇诺"); buyCoffee("摩卡"); 打印出卖出的咖啡 coffeeOrderList.stream().forEach(Order::sell); System.out.println("一共卖出去"+coffeeOrderList.size()+"杯咖啡!"); System.out.println("一共生成了"+CoffeeOrderFactory.getSize()+"个Java对象!"); } 订单列表 static List<Order> coffeeOrderList = Lists.newArrayList(); * 买咖啡 * flavor 口味 buyCoffee(String flavor){ coffeeOrderList.add(CoffeeOrderFactory.getOrder(flavor)); } } 运行结果 卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
一共卖出去12杯咖啡!
一共生成了3个Java对象!
从上面的运行结果可以看出来,虽然卖出去了12杯咖啡,但是最终的口味对象只有3个,因为咖啡口味只有在第一次使用的时候创建,后面就直接使用不会再创建了。 享元模式的分析下面还是来分析一下享元模式的结构吧,结构图如下: ? ? 享元模式涉及到的角色有抽象享元角色、具体享元角色、复合享元角色、享元工厂角色,以及客户端角色。具体说明如下:
享元模式总结单纯享元模式和复合享元模式标准的享元模式中既包含享元对象又包含非享元对象,但是在实际使用过程中我们会用到具体两种特殊形式的享元模式:单纯享元模式和复合享元模式。 单纯享元模式是指,所有的具体享元对象都是可以共享的,不包括非享元对象。 复合享元模式是指,将一些单纯享元对象使用组合模式加以组合,还可以形成组合享元对象,这样的复合享元对象不能共享,但是它可以分解成单纯享元对象,分解后就可以共享了。 享元模式的优点1、可以极大的减少内存中对象的数量,使得相同或相似的对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。 2、享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。 享元模式的缺点1、享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
? ? ? ? ? ? ? ? ? 面试面到怀疑人生,继续加油吧! ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
2、为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
适用场景
一个系统有大量相同或者相似的对象,造成内存的大量耗费。
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
?延伸
在JDK中就有使用享元模式的例子,最常见的就是我们使用的String类,大家都知道String类是被final修饰的,所以不会被继承,每次变更都会生成一个新的字符串,这样就有点占内存了。所以如果直接写出了一个字符串,当后面又写出了一个同样的字符串时会自动去堆中(JDK7以上)查看是否已经存在这个字符串了,如果已经存在则直接使用,如果不存在这个时候才在堆中再给开辟一块空间存储字符串。
例如下面的例子:
运行结果:
?