享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)

一、概念

享元模式是一种结构型设计模式 ,核心思想是:运用共享技术有效地支持大量细粒度对象的复用,从而节省内存。

「享元」= 共享 + 元素

它将对象的状态分为两部分:

状态类型 说明 是否共享
内部状态(Intrinsic State) 对象可共享的、不随环境变化的部分 ✅ 共享
外部状态(Extrinsic State) 随环境变化的、不可共享的部分 ❌ 不共享,由客户端传入

二、结构

复制代码
┌─────────────┐
│   Client     │
└──────┬──────┘
       │
       ▼
┌──────────────────┐       ┌──────────────────┐
│  FlyweightFactory │──────▶│    Flyweight      │  (接口/抽象类)
│  (享元工厂)       │       │  + operation(外部) │
└──────────────────┘       └────────┬─────────┘
       缓存池(Map)                   │
                          ┌─────────┴─────────┐
                          │                     │
                ┌─────────────────┐   ┌──────────────────────┐
                │ ConcreteFlyweight│   │ UnsharedFlyweight     │
                │ (共享的享元对象) │   │ (不共享的享元对象,可选)│
                └─────────────────┘   └──────────────────────┘

三、生活类比 🌳

想象一个围棋游戏

  • 棋盘上可能有上百颗棋子
  • 但棋子只有 两种颜色(内部状态)→ 只需要创建 2 个对象
  • 每颗棋子的位置(x, y) 不同(外部状态)→ 使用时传入

四、代码示例(围棋棋子)

1. 享元接口

java 复制代码
// 享元接口
public interface ChessPiece {
    // 外部状态通过参数传入
    void draw(int x, int y);
}

2. 具体享元类

java 复制代码
// 具体享元 ------ 共享的棋子对象
public class ConcreteChessPiece implements ChessPiece {
    
    // 内部状态:颜色(被共享,不可变)
    private final String color;
    
    public ConcreteChessPiece(String color) {
        this.color = color;
        System.out.println(">>> 创建了一个 [" + color + "] 棋子对象");
    }
    
    @Override
    public void draw(int x, int y) {
        // 外部状态:位置(由客户端传入,不共享)
        System.out.println(color + "棋子落在 (" + x + ", " + y + ")");
    }
}

3. 享元工厂

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class ChessPieceFactory {
    
    // 缓存池:存储已创建的享元对象
    private static final Map<String, ChessPiece> cache = new HashMap<>();
    
    public static ChessPiece getChessPiece(String color) {
        // 如果缓存中已有,则直接返回(共享)
        if (!cache.containsKey(color)) {
            cache.put(color, new ConcreteChessPiece(color));
        }
        return cache.get(color);
    }
    
    public static int getCacheSize() {
        return cache.size();
    }
}

4. 客户端使用

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 下了很多棋子,但实际只创建了 2 个对象
        
        ChessPiece black1 = ChessPieceFactory.getChessPiece("黑");
        black1.draw(3, 4);
        
        ChessPiece white1 = ChessPieceFactory.getChessPiece("白");
        white1.draw(5, 6);
        
        ChessPiece black2 = ChessPieceFactory.getChessPiece("黑");
        black2.draw(7, 8);
        
        ChessPiece white2 = ChessPieceFactory.getChessPiece("白");
        white2.draw(1, 2);
        
        ChessPiece black3 = ChessPieceFactory.getChessPiece("黑");
        black3.draw(9, 10);
        
        // 验证共享
        System.out.println("\n--- 验证 ---");
        System.out.println("black1 == black2 ? " + (black1 == black2));  // true
        System.out.println("white1 == white2 ? " + (white1 == white2));  // true
        System.out.println("实际创建对象数: " + ChessPieceFactory.getCacheSize()); // 2
    }
}

5. 运行输出

java 复制代码
>>> 创建了一个 [黑] 棋子对象
黑棋子落在 (3, 4)
>>> 创建了一个 [白] 棋子对象
白棋子落在 (5, 6)
黑棋子落在 (7, 8)
白棋子落在 (1, 2)
黑棋子落在 (9, 10)

--- 验证 ---
black1 == black2 ? true
white1 == white2 ? true
实际创建对象数: 2

🎯 下了 5 颗棋子,但只创建了 2 个对象! 这就是享元模式的威力。


五、现实中的应用场景

场景 内部状态(共享) 外部状态(不共享)
Java String 常量池 字符串值 引用变量
Java Integer 缓存(-128~127) 数值 使用位置
游戏中的粒子系统 粒子纹理、颜色 位置、速度
文本编辑器的字符渲染 字符字体样式对象 字符在文档中的位置
数据库连接池 连接对象 使用者、使用时间

六、优缺点

✅ 优点

  • 大幅减少内存使用,避免创建大量相似对象
  • 提高系统性能

❌ 缺点

  • 增加系统复杂度:需要分离内部状态和外部状态
  • 享元对象的内部状态必须不可变(线程安全)
  • 读取外部状态会使运行时间略微增加(空间换时间的逆向,这里是时间换空间)

七、一句话总结

享元模式 = 缓存池 + 对象复用。 把不变的部分抽出来共享,把变化的部分作为参数传入,从而用少量对象表示大量实例。

相关推荐
方也_arkling21 小时前
【Java-Day08】static / final / 枚举
java·开发语言
橙淮21 小时前
Spring Bean作用域与生命周期全解析
java·spring
风吹夏回21 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
Chengbei111 天前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_1121 天前
web-第一次课后作业
java·开发语言·idea
小熊Coding1 天前
Python爬取当当网二手图书项目实战!
开发语言·爬虫·python·beautifulsoup·requests·二手图书
秋91 天前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
小江的记录本1 天前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
xiaoshuaishuai81 天前
C# 内存管理与资源泄漏
开发语言·c#
DIY源码阁1 天前
JavaSwing学生成绩管理系统 - MySQL版
java·数据库·mysql·eclipse