设计模式之避免浪费_Flyweight享元模式_共享对象避免浪费
前言
Github:https://github.com/HealerJean
一、享元模式(Flyweight Pattern)
1. 模式概述
享元模式 是一种 结构型设计模式,它通过 共享技术 有效地支持大量细粒度对象的复用,从而 减少内存占用、提升性能。
核心思想:“能共享的就共享,不能共享的就外部传。”
享元模式 = 共享 + 复用 + 节省内存
- 它不是“不用 new”,而是“聪明地 new”。
类比理解:
- String 常量池:
"hello"只创建一次,多次引用同一对象; - 数据库连接池:连接对象昂贵,复用已存在的连接;
2. 使用场景
享元模式适用于以下情况:
- 系统中存在 大量相似对象(数量成千上万);
- 对象的 大部分状态可外部化(即不依赖于具体上下文);
- 对象 创建开销大(如网络连接、图形资源);
- 内存使用成为瓶颈,需优化资源。
典型应用:
- 游戏开发:地图中的树木、草地(相同纹理复用);
- 编辑器:文本字符的字体/颜色格式;
- Web 应用:用户会话中的权限模板、菜单配置;
- Java 中的
Integer.valueOf()、String.intern()。
3. 示例程序:网站分类系统
1) 享元接口 WebSite
/**
* 享元接口(Flyweight)
* operate 方法接收外部状态(如用户、操作内容)
*/
public interface WebSite {
void use(UserContext context); // 外部状态通过参数传入
}
2)具体享元 ConcreteWebSite
/**
* 具体享元(ConcreteFlyweight)
* 内部状态:网站类型(type),不可变且可共享
*/
public class ConcreteWebSite implements WebSite {
private final String type; // 内部状态(共享)
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(UserContext context) {
System.out.printf("【%s】网站正在为用户 %s 执行操作: %s%n",
type, context.getUser(), context.getAction());
}
}
3)外部状态封装 UserContext
/**
* 外部状态(Extrinsic State)
* 每次调用时由客户端提供,不可共享
*/
public class UserContext {
private final String user;
private final String action;
public UserContext(String user, String action) {
this.user = user;
this.action = action;
}
public String getUser() { return user; }
public String getAction() { return action; }
}
4)享元工厂 WebSiteFactory
import java.util.concurrent.ConcurrentHashMap;
/**
* 享元工厂(FlyweightFactory)
* 管理享元对象池,确保相同内部状态只创建一次
*/
public class WebSiteFactory {
private final ConcurrentHashMap<String, WebSite> pool = new ConcurrentHashMap<>();
public WebSite getWebSite(String type) {
return pool.computeIfAbsent(type, ConcreteWebSite::new);
}
public int getTotalWebsites() {
return pool.size();
}
}
5)客户端测试 Main
- 无论多少用户访问“技术”网站,只创建一个
ConcreteWebSite("技术")实例; - 内存节省显著(1000 用户 → 仅 2 个网站对象)。
public class Main {
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
// 多个用户使用“技术”网站(共享同一个 ConcreteWebSite 实例)
WebSite techSite = factory.getWebSite("技术");
techSite.use(new UserContext("张三", "发布 SpringBoot 教程"));
techSite.use(new UserContext("李四", "查看 Kafka 文档"));
// 情感网站
WebSite loveSite = factory.getWebSite("情感");
loveSite.use(new UserContext("王五", "发表恋爱心得"));
// 再次获取“技术”网站(应为同一实例)
WebSite techSite2 = factory.getWebSite("技术");
System.out.println("\n【验证共享】techSite == techSite2 ? " + (techSite == techSite2));
System.out.println("\n网站分类总数: " + factory.getTotalWebsites());
}
}
输出:
【技术】网站正在为用户 张三 执行操作: 发布 SpringBoot 教程
【技术】网站正在为用户 李四 执行操作: 查看 Kafka 文档
【情感】网站正在为用户 王五 执行操作: 发表恋爱心得
【验证共享】techSite == techSite2 ? true
网站分类总数: 2
6)模式角色
| 角色 | 职责 | 示例 |
|---|---|---|
Flyweight(享元接口) |
定义共享对象的接口,方法参数接收外部状态 | WebSite |
ConcreteFlyweight(具体享元) |
实现享元接口,内部状态不可变且可共享 | ConcreteWebSite("技术") |
FlyweightFactory(享元工厂) |
创建并管理享元对象,确保共享 | WebSiteFactory |
Client(客户端) |
维护外部状态,并传递给享元对象 | 调用 operate(user, action) |
4、FQA
1)享元模式 vs 普通工厂/单例
| 模式 | 目的 | 共享粒度 | 适用场景 |
|---|---|---|---|
| 享元模式 | 复用大量相似对象 | 细粒度(按内部状态区分) | 对象多、状态部分可共享 |
| 工厂模式 | 封装对象创建逻辑 | 无共享(每次可能 new) | 解耦创建过程 |
| 单例模式 | 全局唯一实例 | 粗粒度(整个类唯一) | 配置管理、日志器 |
2)模式优点
- 大幅减少内存占用,提升系统性能;
- 支持大量对象 场景(如游戏、图形系统);
- 内部状态可缓存,避免重复计算。
3)注意事项
- 需要分离内外部状态,设计复杂度增加;
- 外部状态需由客户端维护,可能增加调用负担;
- 不适合对象状态完全不同的场景(无法共享);
- 线程安全需额外处理(如使用
ConcurrentHashMap)。


