【设计模式】结构型-享元模式

文章目录

  • 前言
  • 一、概念
  • 二、核心结构
  • [三、Java 代码实现(游戏枪支示例)](#三、Java 代码实现(游戏枪支示例))
    • [1. 抽象享元](#1. 抽象享元)
    • [2. 具体享元(内部状态:枪类型)](#2. 具体享元(内部状态:枪类型))
    • [3. 享元工厂(核心:缓存复用)](#3. 享元工厂(核心:缓存复用))
    • [4. 客户端(外部传入坐标)](#4. 客户端(外部传入坐标))
  • [四、内部状态 vs 外部状态](#四、内部状态 vs 外部状态)
  • 五、优缺点
  • 六、应用场景
  • [七、享元模式 VS 单例模式](#七、享元模式 VS 单例模式)
  • 八、总结

前言

在系统中,如果频繁创建大量相似、重复、细粒度 的对象,会极大消耗内存,导致GC频繁、性能下降。比如:游戏里的子弹、树木;系统中的常量、配置、连接;字符串、线程池、缓存......这些场景都有一个共同点:很多对象是重复的,完全可以复用 。享元模式就是专门解决大量重复对象导致的内存浪费 问题,是性能优化最经典的设计模式之一。


一、概念

享元模式(Flyweight Pattern) 是一种结构型设计模式 ,核心思想:
运用共享技术,高效支持大量细粒度对象的复用,减少内存占用,提升系统性能。

简单理解:

  • 有重复的对象,不重复创建 ,而是缓存起来复用
  • 把对象信息分为两部分:
    1. 内部状态(Intrinsic):可共享、不变、存储在享元内部(如:枪的类型、颜色)
    2. 外部状态(Extrinsic):不可共享、每次不同、由外部传入(如:坐标、伤害)

一句话:能共享的都共享,不能共享的外部传。


二、核心结构

  1. Flyweight(抽象享元)
    定义享元的公共接口,接收并作用于外部状态。
  2. ConcreteFlyweight(具体享元)
    实现接口,包含内部状态,可被共享
  3. FlyweightFactory(享元工厂)
    负责创建、管理、缓存享元对象,确保对象复用。
  4. Client(客户端)
    维护外部状态,通过工厂获取享元并使用。

三、Java 代码实现(游戏枪支示例)

场景:游戏中生成大量相同类型的枪(AK、M4、Sniper),只区分坐标不同。

1. 抽象享元

java 复制代码
public interface Gun {
    // externalState:外部状态(坐标)
    void shoot(int x, int y);
}

2. 具体享元(内部状态:枪类型)

java 复制代码
public class ConcreteGun implements Gun {

    // 内部状态:枪类型(共享)
    private String type;

    public ConcreteGun(String type) {
        this.type = type;
    }

    @Override
    public void shoot(int x, int y) {
        System.out.println("【" + type + "】在坐标(" + x + "," + y + ")射击");
    }
}

3. 享元工厂(核心:缓存复用)

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

public class GunFactory {

    // 缓存池:key=类型,value=享元对象
    private static Map<String, Gun> pool = new HashMap<>();

    public static Gun getGun(String type) {
        // 存在则复用
        if (pool.containsKey(type)) {
            return pool.get(type);
        }
        // 不存在则创建,放入缓存
        Gun gun = new ConcreteGun(type);
        pool.put(type, gun);
        System.out.println("=== 创建新枪:" + type);
        return gun;
    }

    // 查看池中数量
    public static int getGunCount() {
        return pool.size();
    }
}

4. 客户端(外部传入坐标)

java 复制代码
public class Client {
    public static void main(String[] args) {

        Gun gun1 = GunFactory.getGun("AK");
        gun1.shoot(10, 20);

        Gun gun2 = GunFactory.getGun("AK");
        gun2.shoot(30, 40);

        Gun gun3 = GunFactory.getGun("M4");
        gun3.shoot(50, 60);

        Gun gun4 = GunFactory.getGun("M4");
        gun4.shoot(70, 80);

        System.out.println("========================");
        System.out.println("实际创建对象数量:" + GunFactory.getGunCount());
    }
}

输出:

复制代码
=== 创建新枪:AK
【AK】在坐标(10,20)射击
【AK】在坐标(30,40)射击
=== 创建新枪:M4
【M4】在坐标(50,60)射击
【M4】在坐标(70,80)射击
========================
实际创建对象数量:2

射击1000次AK,也只创建1个AK对象


四、内部状态 vs 外部状态

状态 特点 存储位置 示例
内部状态 不变、可共享、不随环境变 享元对象内部 枪类型、颜色、配置
外部状态 变化、不可共享、随环境变 客户端外部传入 坐标、时间、用户ID

享元模式的精髓:分离变与不变。


五、优缺点

优点

  1. 大幅减少对象数量,节约内存,尤其适合大量重复对象。
  2. 统一管理共享对象,便于维护、控制、监控。
  3. 区分内部/外部状态,结构清晰,扩展性强。

缺点

  1. 需要区分内外状态,设计复杂度提高
  2. 共享后对象是全局可见,要注意线程安全。
  3. 维护共享池需要额外开销。

六、应用场景

只要满足:大量重复细粒度对象 + 可区分内外状态,都能用。

  1. 游戏开发
    • 子弹、特效、树木、角色皮肤、地图瓦片
  2. 基础组件
    • 字符串常量池、Integer 缓存、线程池、连接池
  3. 系统优化
    • 配置对象、字典数据、权限项、枚举
  4. UI 组件
    • 字体、颜色、样式、图标复用
  5. 中间件/框架
    • MyBatis 的 SqlSource 缓存
    • Spring 的单例 Bean
    • Tomcat 线程池

最经典:JDK String 常量池 / IntegerCache 就是享元模式!


七、享元模式 VS 单例模式

很多人会混淆,这里一句话区分:

  • 单例:一个类只能有一个对象。
    目的:保证全局唯一,控制实例数。
  • 享元:一个类可以有多个对象,按类型/key共享。
    目的:复用重复对象,节省内存

单例是享元的一种极端情况(只有一个共享对象)。


八、总结

  1. 享元模式 = 对象共享池 + 分离内外状态
  2. 核心:能共享绝不新建,大幅降低内存。
  3. 三要素:抽象享元、具体享元、享元工厂
  4. 最适合:大量重复、细粒度、可分类的对象场景。
  5. 性能优化、内存优化必备设计模式。
相关推荐
sg_knight15 小时前
设计模式实战:命令模式(Command)
python·设计模式·命令模式
渔舟小调21 小时前
P11 | 收藏与行程:用户行为类接口的设计模式
数据库·设计模式·oracle
小程故事多_801 天前
从基础Agent到复杂工作流,LangGraph如何用状态机重构智能体开发
人工智能·设计模式·重构·aigc·ai编程
hypoy1 天前
Claude Code 的 1M Context 怎么用:一篇官方文章的读后整理
设计模式·claude
IT 行者1 天前
软件设计模式会不会是制约大模型编程的障碍?
设计模式·ai编程
t***5441 天前
还有哪些设计模式适合现代C++
开发语言·c++·设计模式
t***5441 天前
如何在现代C++项目中有效应用这些设计模式
开发语言·c++·设计模式
贵慜_Derek1 天前
我们能从 DeerFlow 学到哪些优秀的技术架构设计
人工智能·设计模式·架构
Q741_1471 天前
设计模式之装饰器模式 理论总结 C++代码实战
c++·设计模式·装饰器模式
无籽西瓜a1 天前
【西瓜带你学设计模式 | 第十八期 - 命令模式】命令模式 —— 请求封装与撤销实现、优缺点与适用场景
java·后端·设计模式·软件工程·命令模式