享元模式 (Flyweight Pattern)
什么是享元模式?
享元模式是一种结构型设计模式,它允许你通过共享对象来减少内存使用。享元模式通过共享相似对象的固有状态来减少内存占用。
简单来说:享元模式就是"共享对象",让多个对象共享同一个实例。
生活中的例子
想象一下:
- 五子棋:棋盘上的棋子可以共享,只有位置不同
- 文字编辑器:相同的字符可以共享,只有位置不同
- 游戏中的树:森林中的树可以共享,只有位置不同
为什么需要享元模式?
传统方式的问题
java
// 创建大量相似对象
for (int i = 0; i < 10000; i++) {
Tree tree = new Tree("松树", "绿色");
tree.setPosition(i, i);
}
问题:
- 内存浪费:大量相似对象占用大量内存
- 性能下降:创建和销毁大量对象影响性能
享元模式的优势
java
// 共享对象
TreeFactory factory = new TreeFactory();
for (int i = 0; i < 10000; i++) {
Tree tree = factory.getTree("松树", "绿色");
tree.setPosition(i, i);
}
优势:
- 减少内存:通过共享对象减少内存使用
- 提高性能:减少对象的创建和销毁
享元模式的结构
┌─────────────────────┐
│ Flyweight │ 享元接口
├─────────────────────┤
│ + operation(): void │
└──────────┬──────────┘
│ 实现
│
┌──────────┴──────────┐
│ ConcreteFlyweight │ 具体享元
├─────────────────────┤
│ - intrinsicState │
│ + operation(): void │
└─────────────────────┘
┌─────────────────────┐
│ FlyweightFactory │ 享元工厂
├─────────────────────┤
│ - flyweights: Map │
│ + getFlyweight(): │
│ Flyweight │
└─────────────────────┘
代码示例
1. 定义享元接口
java
/**
* 享元接口:树
*/
public interface Tree {
/**
* 显示树
* @param x x坐标
* @param y y坐标
*/
void display(int x, int y);
}
2. 定义具体享元
java
/**
* 具体享元:树
*/
public class ConcreteTree implements Tree {
private String type;
private String color;
public ConcreteTree(String type, String color) {
this.type = type;
this.color = color;
}
@Override
public void display(int x, int y) {
System.out.println(type + "树(" + color + ") 在位置 (" + x + ", " + y + ")");
}
}
3. 定义享元工厂
java
/**
* 享元工厂:树工厂
*/
public class TreeFactory {
private Map<String, Tree> trees = new HashMap<>();
/**
* 获取树
*/
public Tree getTree(String type, String color) {
String key = type + "-" + color;
Tree tree = trees.get(key);
if (tree == null) {
tree = new ConcreteTree(type, color);
trees.put(key, tree);
System.out.println("创建新的" + type + "树(" + color + ")");
}
return tree;
}
/**
* 获取树对象数量
*/
public int getTreeCount() {
return trees.size();
}
}
4. 使用享元
java
/**
* 享元模式测试类
* 演示如何使用享元模式减少内存使用
*/
public class FlyweightTest {
public static void main(String[] args) {
System.out.println("=== 享元模式测试 ===\n");
TreeFactory factory = new TreeFactory();
// 创建10000棵树
System.out.println("--- 创建10000棵树 ---");
for (int i = 0; i < 10000; i++) {
Tree tree = factory.getTree("松树", "绿色");
tree.display(i, i);
}
System.out.println("\n实际创建的树对象数量: " + factory.getTreeCount());
// 创建不同类型的树
System.out.println("\n--- 创建不同类型的树 ---");
Tree tree1 = factory.getTree("松树", "绿色");
Tree tree2 = factory.getTree("松树", "绿色");
Tree tree3 = factory.getTree("柳树", "绿色");
Tree tree4 = factory.getTree("枫树", "红色");
tree1.display(0, 0);
tree2.display(1, 1);
tree3.display(2, 2);
tree4.display(3, 3);
System.out.println("\n实际创建的树对象数量: " + factory.getTreeCount());
System.out.println("\n=== 享元模式的优势 ===");
System.out.println("1. 减少内存:通过共享对象减少内存使用");
System.out.println("2. 提高性能:减少对象的创建和销毁");
System.out.println("3. 资源共享:多个对象可以共享资源");
System.out.println("\n=== 实际应用场景 ===");
System.out.println("1. 字符串常量池:Java中的字符串常量池");
System.out.println("2. Integer缓存:Java中的Integer缓存");
System.out.println("3. 游戏开发:游戏中的角色、道具等");
System.out.println("4. 图形处理:共享相同的图形对象");
System.out.println("\n=== 内存节省示例 ===");
System.out.println("如果不使用享元模式:");
System.out.println(" - 创建10000棵树需要10000个对象");
System.out.println("使用享元模式:");
System.out.println(" - 创建10000棵树只需要" + factory.getTreeCount() + "个对象");
System.out.println(" - 节省了 " + (10000 - factory.getTreeCount()) + " 个对象");
}
}
享元模式的优点
- 减少内存:通过共享对象减少内存使用
- 提高性能:减少对象的创建和销毁
- 资源共享:多个对象可以共享资源
享元模式的缺点
- 增加复杂度:引入了额外的类
- 状态分离:需要分离内部状态和外部状态
适用场景
- 大量相似对象:需要创建大量相似对象
- 内存敏感:对内存使用敏感
- 对象可共享:对象可以被共享
常见应用场景
- 字符串常量池:Java中的字符串常量池
- Integer缓存:Java中的Integer缓存
- 游戏开发:游戏中的角色、道具等
使用建议
- 大量相似对象:使用享元模式
- 内存敏感:使用享元模式
- 少量对象:直接创建即可
注意事项
⚠️ 享元模式虽然有用,但要注意:
- 区分内部状态和外部状态
- 确保对象是线程安全的