享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用和提高性能,特别适用于处理大量相似对象的场景。其核心思想是将对象的内部状态(可共享的部分)和外部状态(不可共享的部分)分离,通过共享内部状态来避免重复创建相似对象。
核心概念
- Flyweight(享元接口)
定义对象可共享的方法,通常包含一个接收外部状态参数的方法。 - ConcreteFlyweight(具体享元类)
实现享元接口,存储内部状态(可共享的部分)。 - FlyweightFactory(享元工厂)
管理享元对象的池(缓存),确保对象被正确共享。 - Client(客户端)
维护外部状态(不可共享的部分),并在调用享元对象时传递外部状态。
应用场景
- 存在大量相似对象,且这些对象的状态可分离为内部和外部部分。
- 内存占用高,且大部分对象的内部状态可以共享。
- 例如:游戏中的粒子系统、文本编辑器中的字符渲染、线程池/连接池等。
框架中的使用案例
1. Java 字符串常量池
- 机制:Java 通过字符串常量池(String Pool)复用字符串字面量,避免重复创建相同字符串。
- 享元模式体现:字符串是不可变的(内部状态),相同的字符串字面量指向池中的同一对象。
String s1 = "hello"; // 从常量池获取 String s2 = "hello"; // 复用 s1 的对象 System.out.println(s1 == s2); // true
2. Java 包装类的缓存(如 Integer)
- 机制:Java 对部分值范围(如 -128 到 127)的
Integer
对象进行缓存。 - 享元模式体现:调用
Integer.valueOf()
时,优先返回缓存对象。Integer a = Integer.valueOf(127); // 从缓存获取 Integer b = Integer.valueOf(127); // 复用 a 的对象 System.out.println(a == b); // true
3. GUI 框架中的单元格渲染(如 JavaFX、Swing)
- 场景:表格或列表中大量单元格的渲染。
- 享元模式体现:复用单元格渲染器对象,仅更新外部状态(如单元格数据)。
// 伪代码示例:表格单元格渲染器 TableCellRenderer renderer = factory.getRenderer("text"); renderer.render(dataRow, context); // dataRow 是外部状态
4. 线程池/数据库连接池
- 机制:预先创建一组线程或连接对象,避免频繁创建销毁。
- 享元模式体现:线程/连接对象是内部状态,任务/请求是外部状态。
5. 游戏引擎中的粒子系统
- 场景:游戏中的爆炸、火焰等粒子效果需要渲染大量相似粒子。
- 享元模式体现:共享粒子的纹理、颜色等内部状态,仅更新位置、速度等外部状态。
6. Spring 框架中的 Bean 作用域
- 单例作用域:Spring 默认单例模式的 Bean 被所有请求共享,类似于享元模式。
- 原型作用域:非共享对象,每次请求创建新实例。
优缺点
- 优点:
- 减少内存占用,提升性能。
- 集中管理共享状态,避免重复。
- 缺点:
- 增加系统复杂性(需分离内部/外部状态)。
- 线程安全问题需谨慎处理。
总结
享元模式通过共享不可变状态优化资源使用,非常适合处理大量相似对象的场景。在框架中,它常见于常量池、连接池、UI 组件复用等场景。使用时需注意状态分离和线程安全。