设计模式-享元模式
# 设计模式-享元模式
# 概述
如果一个软件系统在运行时所创建的相同或相似对象数量太多,将导致运行代价过高,带来系统资源浪费、性能下降等问题。
例如在一个文本字符串中存在很多重复的字符,如果每一个字符都用一个单独的对象来表示,将会占用较多的内存空间,那么如何避免系统中出现大量相同或相似的对象。
# 定义
当系统中存在大量相同或者相似的对象时,享元模式通过共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间、提高了系统性能。
享元模式: 运用共享技术有效地支持大量细粒度对象的复用。
# 享元模式结构
享元模式包含以下4个角色:
UnsharedConcreteFlyweight 是非享元角色,里面包含了非共享的外部状态信息 info;
Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight state),非享元的外部状态以参数的形式通过该方法传入;
ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;
FlyweightFactory 是享元工厂角色,它通过关键字 key 来管理具体享元;
# 享元模式应用案例
我们来实现一个圆形工厂,对于相同颜色的圆形我们就只创建一次,后续颜色相同的圆形直接使用之前创建的。
1、创建一个图形接口
public interface Shape {
void draw();
}
2、创建圆形
public class Circle implements Shape{
private final String color;
private int x;
private int y;
private int radius;
public Circle(String color) {
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color
+ ", x : " + x + ", y :" + y + ", radius :" + radius);
}
}
3、创建圆形工厂
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}
4、案例实现
public class FlyweightPatternDemo {
private static final String[] colors = {"Red", "Green", "Blue", "White", "Black"};
public static void main(String[] args) {
for (int i = 0; i < 20; ++i) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
private static int getRandomX() {
return (int) (Math.random() * 100);
}
private static int getRandomY() {
return (int) (Math.random() * 100);
}
}
// 输出
Creating circle of color : Black
Circle: Draw() [Color : Black, x : 23, y :60, radius :100
Circle: Draw() [Color : Black, x : 97, y :50, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 52, y :25, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 46, y :90, radius :100
Circle: Draw() [Color : Green, x : 25, y :23, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 91, y :33, radius :100
Circle: Draw() [Color : Black, x : 2, y :73, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 56, y :60, radius :100
Circle: Draw() [Color : Red, x : 21, y :86, radius :100
Circle: Draw() [Color : White, x : 70, y :62, radius :100
Circle: Draw() [Color : Red, x : 47, y :64, radius :100
Circle: Draw() [Color : Green, x : 24, y :76, radius :100
Circle: Draw() [Color : Black, x : 95, y :8, radius :100
Circle: Draw() [Color : White, x : 35, y :88, radius :100
Circle: Draw() [Color : Green, x : 94, y :63, radius :100
Circle: Draw() [Color : Red, x : 12, y :1, radius :100
Circle: Draw() [Color : White, x : 36, y :2, radius :100
Circle: Draw() [Color : Red, x : 34, y :35, radius :100
Circle: Draw() [Color : Blue, x : 64, y :52, radius :100
Circle: Draw() [Color : Red, x : 54, y :14, radius :100
# 享元模式优缺点
优点:
- 享元模式可以减少内存中对象的数量,使得相同或者相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
- 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使享元对象可以在不同环境中被共享。
缺点:
- 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
- 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使运行时间变长。
# 享元模式使用环境
- 一个系统中有大量相同或者相似的对象,造成内存的大量耗费。
- 对象的大部分状态可以外部化,可以将这些外部状态传入对象中。
- 在使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此应当在需要多次重复使用享元对象时才使用享元对象。
# 享元模式在Java中的运用
# Integer
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
public class FlyweightPatternJavaInteger {
public static void main(String[] args) {
Integer x = Integer.valueOf(127);
Integer y = Integer.valueOf(127);
Integer z = new Integer(127);
System.out.println(x == y);
System.out.println(x == z);
Integer m = Integer.valueOf(200);
Integer n = Integer.valueOf(200);
System.out.println(m == n);
}
}
// 输出
true
false
false
小结:
1、在valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从缓存池返回
2、IntegerCache范围在[-128, 127]之间。
3、如果超出这个范围,valueOf方法就会产生一个新的对象。
# String
public class FlyweightPatternJavaString {
public static void main(String[] args) {
String s1 = "wxx";
String s2 = "wxx";
String s3 = new String("wxx");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
}
}
// 输出
true
false
private final char value[];
# 参考
Java设计模式
上次更新: 2024/06/29, 15:13:44