享元模式的原理的详细解析以及使用案例。

前言

享元模式的核心是共享细粒度对象,以减少内存使用和对象创建的开销。这和对象池有相似之处,但享元更侧重于共享内在状态,而将可变部分作为外部状态传递。

关键点包括内在状态和外在状态的区分,工厂类管理共享对象,以及如何实现对象的复用。

享元模式可以作为另一种工具,特别是在处理大量相似对象时。比如,游戏中大量重复的棋子类型,或者文档中的字符对象,这些场景下享元模式非常适用。

需要注意的是,享元模式适用于对象的大部分状态可以外部化的情况。如果对象的状态经常变化,可能不适合使用享元。此外,线程安全也是一个考虑点,因为共享对象可能被多个线程访问。

最后,总结适用场景和优缺点,帮助用户判断何时使用享元模式。例如,在需要处理大量相似对象且内存占用高时,享元模式能显著优化性能。但如果对象状态变化频繁或难以区分内外状态,可能不适用。


享元模式(Flyweight Pattern)原理详解

核心思想 :通过共享技术减少大量细粒度对象的创建开销,将对象的**内在状态(不变部分)外在状态(可变部分)**分离,仅存储一份内在状态,外在状态由客户端传递。

关键概念
  1. 内在状态(Intrinsic State)

    • 对象中不变的部分,可以被多个对象共享(如字体、颜色配置)。
    • 存储于享元对象内部。
  2. 外在状态(Extrinsic State)

    • 对象中随上下文变化的部分(如位置、尺寸)。
    • 由客户端在调用时传入,不存储在享元对象中。
  3. 享元工厂(Flyweight Factory)

    • 管理共享的享元对象池,确保相同内在状态的对象只创建一次。

享元模式结构

text 复制代码
+-------------------+         +---------------------+
|   Flyweight       |         | FlyweightFactory    |
+-------------------+         +---------------------+
| +operation(extr)  |<------->| -pool: Map<String,Flyweight> |
+-------------------+         | +getFlyweight(key)  |
          ^                  +---------------------+
          |                              
+-------------------+
| ConcreteFlyweight |
+-------------------+
| -intrinsicState   |
+-------------------+
| +operation(extr)  |
+-------------------+

代码示例:文本编辑器中的字符对象

假设需要渲染大量文本字符,每个字符的字体、颜色等属性固定(内在状态),但位置、大小可变(外在状态)。

1. 定义享元接口
java 复制代码
public interface CharacterFlyweight {
    void render(int x, int y); // 外在状态由参数传递
}
2. 实现具体享元类
java 复制代码
public class ConcreteCharacter implements CharacterFlyweight {
    // 内在状态(不变)
    private final char character;
    private final String font;
    private final String color;

    public ConcreteCharacter(char character, String font, String color) {
        this.character = character;
        this.font = font;
        this.color = color;
    }

    @Override
    public void render(int x, int y) {
        System.out.printf("Render '%s' at (%d,%d) with font=%s, color=%s\n",
                character, x, y, font, color);
    }
}
3. 实现享元工厂
java 复制代码
import java.util.HashMap;
import java.util.Map;

public class CharacterFactory {
    private static final Map<String, CharacterFlyweight> pool = new HashMap<>();

    public static CharacterFlyweight getCharacter(char c, String font, String color) {
        // 生成唯一键:组合内在状态
        String key = c + "-" + font + "-" + color;
        if (!pool.containsKey(key)) {
            pool.put(key, new ConcreteCharacter(c, font, color));
        }
        return pool.get(key);
    }
}
4. 客户端使用
java 复制代码
public class Client {
    public static void main(String[] args) {
        // 获取共享的享元对象
        CharacterFlyweight charA = CharacterFactory.getCharacter('A', "Arial", "Red");
        CharacterFlyweight charB = CharacterFactory.getCharacter('B', "Times New Roman", "Blue");
        CharacterFlyweight charA2 = CharacterFactory.getCharacter('A', "Arial", "Red"); // 复用 charA

        // 渲染字符,传递外在状态(位置)
        charA.render(10, 20);
        charB.render(30, 40);
        charA2.render(50, 60); // 复用 charA 的内在状态
    }
}
输出结果
text 复制代码
Render 'A' at (10,20) with font=Arial, color=Red
Render 'B' at (30,40) with font=Times New Roman, color=Blue
Render 'A' at (50,60) with font=Arial, color=Red

享元模式 vs. 对象池

特性 享元模式 对象池
目标 减少内存占用,共享不变状态 复用可变对象,减少创建开销
状态管理 内在状态共享,外在状态由客户端传递 对象状态可能被重置或复用
适用场景 大量相似对象,内在状态可分离(如字符、UI控件) 对象创建成本高(如数据库连接、线程)

适用场景

  1. 大量重复对象:如游戏中的粒子系统、文本编辑器中的字符。
  2. 内存敏感型应用:移动端或嵌入式系统。
  3. 不可变对象:如
相关推荐
草莓熊Lotso21 分钟前
【Linux网络】从0手写Reactor反应堆(二):完善核心细节——ET非阻塞读写、分层架构与回调机制
linux·运维·服务器·网络·c++·tcp/ip·架构
未秃头的程序猿24 分钟前
凌晨3点被叫醒:线上OOM,我用这套流程40分钟定位根因
java·jvm·后端
letisgo541 分钟前
JDK21 + Spring AI 2.0 入场指南:2026 年 Java 开发者 AI 架构全景图
java·ai·idea·jdk21
白仑色44 分钟前
Spring Boot 从切面统一控制事务
java·spring boot·aop·spring事务
Flynt1 小时前
Java并行流,让我debug了一整天
java
xlq223221 小时前
高并发服务器day21
运维·服务器
DFT计算杂谈1 小时前
FeSe超薄膜在CaF2衬底上的电子结构DFT研究
java·服务器·前端
Brilliantwxx1 小时前
【Linux】 第一个程序终端进度条
linux·运维·服务器
circuitsosk1 小时前
长文本与高并发下的Token“瘦身”策略:Prompt压缩与上下文窗口优化
java·前端·python·prompt·上下文窗口·token优化
岁岁养乐多1 小时前
LangChain4j 工厂模式
java·开发语言