Java设计模式之享元模式
概念
享元模式是一种结构型设计模式,通过共享对象来减少内存占用和对象创建开销。它将对象状态分为:
1.内部状态(Intrinsic):可共享的、不变的部分(如颜色)。
2.外部状态(Extrinsic):不可共享的、变化的部分(如位置、半径)。
作用
1.减少重复对象的创建,降低内存消耗。
2.提高系统性能(对象初始化成本高时效果显著)。
使用场景
1.系统需要创建大量相似对象。
2.对象的大部分状态可以外部化。
3.需要缓存或对象池的场景(如游戏中的粒子系统、文档编辑器中的字符对象)。
举例
// 享元接口
interface Circle {
void draw(int x, int y, int radius);
}
// 具体享元类
class ConcreteCircle implements Circle {
private final String color; // 内部状态(共享)
public ConcreteCircle(String color) {
this.color = color;
}
@Override
public void draw(int x, int y, int radius) { // 外部状态作为参数
System.out.printf("Drawing %s circle at (%d,%d) with radius %d\n",
color, x, y, radius);
}
}
// 享元工厂
class CircleFactory {
private static final Map<String, Circle> circleMap = new HashMap<>();
public static Circle getCircle(String color) {
// 如果没有生成特定颜色的圆,则生成并保存,最后再返回
if (!circleMap.containsKey(color)) {
Circle newCircle = new ConcreteCircle(color);
circleMap.put(color, newCircle);
}
return circleMap.get(color);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
String[] colors = {"Red", "Blue", "Green"};
// 获取10个圆形,但实际只创建3个对象
for(int i=0; i<10; i++){
String color = colors[i % 3];
Circle circle = CircleFactory.getCircle(color);
circle.draw(i*10, i*20, 5); // 外部状态每次不同
}
}
}
优点和缺点
优点
1.减少内存使用。
2.提高性能(减少GC压力)。
3.适用于大量相似对象的场景。
缺点
1.增加系统复杂性。
2.需要区分内部/外部状态。
3.可能引入线程安全问题。
未使用享元模式的实现
class SimpleCircle {
private String color;
public SimpleCircle(String color) {
this.color = color;
}
public void draw(int x, int y, int radius) {
System.out.printf("Drawing %s circle at (%d,%d) with radius %d\n",
color, x, y, radius);
}
}
public class Client {
public static void main(String[] args) {
String[] colors = {"Red", "Blue", "Green"};
// 每次都会创建新对象(共创建10个对象)
for(int i=0; i<10; i++){
String color = colors[i % 3];
SimpleCircle circle = new SimpleCircle(color); // 每次都新建对象
circle.draw(i*10, i*20, 5);
}
}
}
总结
1.享元核心:通过对象复用将对象数量从使用次数级别降低到不同状态组合级别。
2.取舍判断:当对象初始化成本高且存在大量重复状态时优先使用。
3.状态管理:需要仔细区分类的固有属性和运行时可变属性。
4.性能影响:在需要创建数百万个对象的场景中,内存节省效果会非常显著。
实际开发中,Java的String
常量池、Integer.valueOf()
缓存(-128~127)都是享元模式的经典应用。
享元模式和单例模式的区别
特征 | 享元模式 | 单例模式 |
核心目标 | 通过共享多个对象减少内存占用 | 确保全局唯一对象 |
对象数量 | 按内部状态分类创建多个共享对象 | 整个系统只存在一个实例 |
状态管理 | 区分内部状态(共享)和外部状态 | 通常不区分状态,或状态全局共享 |
创建方式 | 工厂根据参数返回不同共享对象 | 静态方法返回唯一实例 |
适用场景 | 需要管理大量相似对象的场景 | 需要全局唯一访问点的场景 |
典型应用 | 游戏粒子系统、文档编辑器字符对象 | 数据库连接池、配置管理器、日志工具 |