前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、享元模式(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)。