深入探索Java设计模式(四)之享元模式
享元模式适用于需要大量相同类型对象的情况。在此,设计布局可以减少创建多个对象的方式。对象在运行时会消耗资源,因此最好在内存中使用较少的对象。它减少了内存占用并利用了程序的整体性能。本文是在学习完优锐课JAVA架构VIP课程—【框架源码专题】中《学习源码中的优秀设计模式》后写下的学习感悟。通过Java中的一个简单示例深入研究了这种模式。
总览
轻量级模式减少了重复数据的使用。通常的做法是将对象设计成细粒度以利用其使用的灵活性。但是,问题是对象在运行时会消耗资源。粒度会不利地影响应用程序的性能,因为需要将运行时对象容纳在更大的内存集中。每个对象不仅消耗内存,而且消耗CPU周期。在这种情况下,轻量化模式可以通过减少细粒度对象的重复实例化来解决。
享元模式Flyweight Pattern
享元模式分为两部分:
- 内部的:这是独立的部分,数据存储在对象中,与享元模式上下文无关。结果,它对于所有相同的固有数据都是可共享的,因此可以用享元模式对象替换。
- 外在的: 这是从属部分,其中保留了举重对象的上下文信息。这是不可共享的。状态由客户端对象存储,并根据要求传递给享元模式对象。
享元模式的使用
在我们有许多表示相同值的对象的情况下,这很有用。因此,它使不可变对象的值可共享。例如,在下面的代码片段中观察Java标准库的Integer包装器类的valueOf()方法如何检查给定参数的值。
1 public static Integer valueOf(int i) { 2 if (i >= IntegerCache.low && i <= IntegerCache.high) 3 return IntegerCache.cache[i + (-IntegerCache.low)]; 4 return new Integer(i); 5 }
如果该值先前已缓存,则该方法将返回构造函数实例,而不是创建一个新对象。因为缓存是在静态块中初始化的,所以仅在第一次调用时才创建Integer实例。请注意,IntegerCache类是在Integerclass中定义的私有静态类。
1 private static class IntegerCache { 2 // ... 3 static { 4 // ... 5 cache = new Integer[(high - low) + 1]; 6 int j = low; 7 for (int k = 0; k < cache.length; k++) 8 cache[k] = new Integer(j++); 9 // Range [-128, 127] must be interned (JLS7 5.1.7) 10 assert IntegerCache.high >= 127; 11 } 12 // ... 13 }
现在,我们可以测试Integer类是否创建两个不同的对象(如果值相同)。将图1中的输出与以下代码进行比较。
1 public class JustForTesting { 2 public static void main(String[] args){ 3 final Integer intObj1 = Integer.valueOf(10); 4 final Integer intObj2 = Integer.valueOf(10); 5 System.out.println("First Case"); 6 if (intObj1 == intObj2){ 7 System.out.println("If the values are same " + 8 "then the objects are also same."); 9 } else { 10 System.out.println("If the values are different " + 11 "then the objects are also distinct."); 12 } 13 // Now, if we change the same value to a different 14 // value, it becomes two distinct objects 15 16 final Integer intObj3 = Integer.valueOf(10); 17 final Integer intObj4 = Integer.valueOf(20); 18 System.out.println("Second Case"); 19 if (intObj3 == intObj4){ 20 System.out.println("If the values are same " + 21 "then the objects are also same."); 22 } else { 23 System.out.println("If the values are different " + 24 "then the objects are also distinct."); 25 } 26 } 27 }
输出
图1:前面代码的输出
实现享元模式
这是享元模式的简单实现。观察如何限制具有相同值的对象创建新对象,而不是提供池中已经存在的对象的副本。工厂中的对象池充当保留享元模式实例的缓存。
1 package testpattern; 2 public class Car { 3 private String color; 4 public Car(String color){ 5 this.color = color; 6 System.out.println("Painted with "+color+" color."); 7 } 8 public String getColor() { 9 return color; 10 } 11 } 12 package testpattern; 13 import java.util.ArrayList; 14 import java.util.List; 15 public class CarFactory { 16 private List<Car> carpool = new ArrayList<>(); 17 public Car getFlyweightCar(String color) { 18 for (Car c: carpool) { 19 if (c.getColor().equals(color)){ 20 System.out.println(color + 21 " car is already in the pool!"); 22 return c; 23 } 24 } 25 Car car = new Car(color); 26 carpool.add(car); 27 return car; 28 } 29 } 30 package testpattern; 31 public class App { 32 public static void main(String[] args) { 33 CarFactory cf = new CarFactory(); 34 cf.getFlyweightCar("RED"); 35 cf.getFlyweightCar("BLUE"); 36 cf.getFlyweightCar("GREEN"); 37 cf.getFlyweightCar("PURPLE"); 38 cf.getFlyweightCar("RED"); 39 cf.getFlyweightCar("BLUE"); 40 cf.getFlyweightCar("BLACK"); 41 } 42 }
输出
图2:享元模式实现的输出
结论
享元模式是在许多情况下使用的常见模式,有时程序员会在不知不觉中使用它。这种模式说明了我们如何使用许多内存不足的对象。如前所述,我们必须始终尽可能利用不变的Integer类的valueOf方法,因为当我们调用new时,即使缓存包含相同的对象,也会创建一个新实例。正如我们已经讨论过的那样,valueOf方法还可以工作。这是使用享元模式的优势。
感谢阅读!欢迎留言。想更深入探讨学习也欢迎私信我。下篇继续~