设计模式——享元设计模式(结构型)

摘要

享元设计模式是一种结构型设计模式,旨在通过共享对象减少内存占用和提升性能。其核心思想是将对象状态分为内部状态(可共享)和外部状态(不可共享),并通过享元工厂管理共享对象池。享元模式包含抽象享元类、具体享元类、非共享具体享元类和享元工厂类。它适用于处理大量相似对象的场景,如文档编辑器中的字符对象。文章还提供了享元模式的实现方式、适合与不适合的使用场景、实战示例以及与其他设计模式的比较。

1. 享元设计模式定义

享元设计模式(Flyweight Pattern) 是一种结构型设计模式,用于减少对象的数量,以节省内存和提高性能。享元模式通过共享内存中已经存在的对象,避免重复创建相同内容的对象,适用于大量相似对象的场景。

1.1.1. 核心思想

把对象状态划分为:

  • 内部状态(可共享,存储在享元对象中)
  • 外部状态(不可共享,由客户端维护)

享元工厂(FlyweightFactory): 负责管理共享对象的池,复用已有实例。

1.1.2. 举例说明

假设一个文档编辑器中有 10 万个字符,但字符集就 128 个(ASCII),如果每个字符都是独立对象,将浪费大量内存。此时可以:

  • 把字符内容作为内部状态(可共享)
  • 把字体、大小、颜色作为外部状态(由外部控制)
  • 同一个字符内容只创建一次对象,由享元工厂复用

2. 享元设计模式结构

享元模式包含如下角色:

  • Flyweight: 抽象享元类
  • ConcreteFlyweight: 具体享元类
  • UnsharedConcreteFlyweight: 非共享具体享元类
  • FlyweightFactory: 享元工厂类

2.1. 享元设计模式类图

2.2. 享元设计模式时序图

3. 享元设计模式实现方式

享元设计模式的实现方式主要围绕 对象共享池内部状态与外部状态的分离。它通过一个享元工厂(FlyweightFactory)来管理共享对象,避免重复创建,从而节省内存。

3.1. 1️⃣ 定义抽象享元接口(Flyweight)

public interface Flyweight {void operation(String externalState); // 外部状态由调用方传入
}

3.2. 2️⃣ 创建具体享元类(ConcreteFlyweight)

public class ConcreteFlyweight implements Flyweight {private final String intrinsicState; // 内部状态,能共享public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void operation(String externalState) {System.out.println("共享[" + intrinsicState + "],非共享[" + externalState + "]");}
}

3.3. 3️⃣ 创建享元工厂类(FlyweightFactory)

public class FlyweightFactory {private final Map<String, Flyweight> pool = new HashMap<>();public Flyweight getFlyweight(String key) {if (!pool.containsKey(key)) {pool.put(key, new ConcreteFlyweight(key));}return pool.get(key);}public int getPoolSize() {return pool.size();}
}

3.4. 4️⃣ 客户端调用(Client)

public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight a1 = factory.getFlyweight("A");Flyweight a2 = factory.getFlyweight("A"); // 重复,不创建新对象a1.operation("外部状态1");a2.operation("外部状态2");System.out.println("共享对象数量:" + factory.getPoolSize()); // 输出:1}
}

3.5. ✅ 享元设计模式总结

要素

说明

内部状态

可以共享,存储在享元对象中,如字符、类型

外部状态

每次调用时由客户端传入,不在享元内部存储

工厂类

管理共享对象的创建与复用

缓存池

使用 HashMap 或 ConcurrentHashMap 存储共享对象

线程安全注意点

在并发环境下需保证工厂创建逻辑线程安全(如加锁或使用 ConcurrentMap)

4. 享元设计模式适合场景

4.1. ✅ 适合使用享元设计模式的场景

场景

说明

大量重复对象需创建,且状态大部分相同

比如:文字编辑器中的字符对象、地图中的草地格子、游戏中的粒子等,能显著减少内存开销。

对象创建成本高,希望通过复用来减少系统负担

比如:金融系统中共享的“黑名单规则”、“风控维度元数据”等。

对象状态可拆分为可共享的内部状态和不可共享的外部状态

共享部分放入享元,变动部分交给外部传入,从而实现高复用。

系统中存在大量细粒度对象,结构相似、功能一致

如图形系统中的形状节点、文档编辑器中的字体、格式等。

缓存池或对象池机制的实现场景

比如:数据库连接池、线程池、元数据池、图标池、缓存字典等。

4.2. ❌ 不适合使用享元模式的场景

场景

原因

对象状态频繁变化,不可拆分共享/不共享状态

对象状态不能被提取为外部状态时,就无法有效共享,甚至会导致共享污染。

对象之间差异太大,无法复用或没有可共享部分

如果每个对象都是完全不同的个体,享元模式无法带来价值。

系统对对象独立性要求高,不允许共享

比如线程不安全或敏感业务逻辑要求每个对象独立维护生命周期。

对象数量本身不多,内存开销可以接受

引入享元结构反而增加了系统复杂度、调试难度,不值得。

共享对象内部包含资源引用,如 Socket、File 等

资源不允许多个业务共享,使用享元会造成资源冲突或数据错乱。

4.3. 🧠 享元模式的场景总结

项目

适合使用享元模式

不适合使用享元模式

对象数量

✅ 大量重复

❌ 数量少

状态结构

✅ 可拆分内外状态

❌ 状态复杂或强耦合

系统压力

✅ 内存敏感,需优化

❌ 性能足够,优化收益低

可复用性

✅ 可复用部分明显

❌ 无明显共享逻辑

系统复杂度

✅ 有控制成本价值

❌ 小项目/临时代码

5. 享元设计模式实战示例

下面是一个 享元设计模式在金融风控系统中的 Spring 实战示例,场景为:风控规则元数据共享池,用于缓存和复用规则的静态定义,减少重复加载和内存占用。在风控系统中,不同的策略规则经常引用相同的“规则定义”(如规则编号、描述、字段映射等)。这些定义是 不可变 的、重复使用 的,适合使用享元模式来缓存复用。

5.1. ✅ 享元接口:RuleDefinition

public interface RuleDefinition {void evaluate(String param);  // 示例行为
}

5.2. ✅ 具体享元类:ConcreteRuleDefinition

public class ConcreteRuleDefinition implements RuleDefinition {private final String ruleCode;private final String description;public ConcreteRuleDefinition(String ruleCode, String description) {this.ruleCode = ruleCode;this.description = description;}@Overridepublic void evaluate(String param) {System.out.println("执行规则 [" + ruleCode + "] - " + description + ",参数:" + param);}public String getRuleCode() {return ruleCode;}public String getDescription() {return description;}
}

5.3. ✅ 享元工厂:RuleDefinitionFactory(由 Spring 管理)

@Component
public class RuleDefinitionFactory {private final Map<String, RuleDefinition> pool = new ConcurrentHashMap<>();/*** 获取共享的规则定义*/public RuleDefinition getRule(String ruleCode) {return pool.computeIfAbsent(ruleCode, this::loadRuleDefinition);}/*** 模拟从数据库或配置中加载规则元数据*/private RuleDefinition loadRuleDefinition(String ruleCode) {// 实际情况应从数据库或配置中心加载System.out.println("加载规则定义:" + ruleCode);return new ConcreteRuleDefinition(ruleCode, "规则描述_" + ruleCode);}public int getPoolSize() {return pool.size();}
}

5.4. ✅ 客户端服务:RiskEngineService(注入使用)

@Service
public class RiskEngineService {@Autowiredprivate RuleDefinitionFactory ruleDefinitionFactory;public void processRisk(String ruleCode, String inputParam) {RuleDefinition rule = ruleDefinitionFactory.getRule(ruleCode);rule.evaluate(inputParam);}
}

5.5. ✅ 启动类或控制器测试(模拟调用)

@RestController
public class RiskController {@Autowiredprivate RiskEngineService riskEngineService;@GetMapping("/risk/test")public String test() {riskEngineService.processRisk("R001", "用户A数据");riskEngineService.processRisk("R001", "用户B数据");riskEngineService.processRisk("R002", "用户C数据");return "风控规则执行完毕";}
}

5.6. ✅ 运行结果示例

加载规则定义:R001
执行规则 [R001] - 规则描述_R001,参数:用户A数据
执行规则 [R001] - 规则描述_R001,参数:用户B数据
加载规则定义:R002
执行规则 [R002] - 规则描述_R002,参数:用户C数据

可见:R001 只加载一次,后续复用;实现了享元模式在 Spring 项目下的实战落地。

要素

说明

享元类

ConcreteRuleDefinition

享元工厂

RuleDefinitionFactory

共享池

ConcurrentHashMap缓存规则

注解注入

使用 @Autowired,不采用构造函数注入

应用场景

风控规则元数据复用、规则模板复用、评分模型共享

6. 享元设计模式思考

6.1. 享元设计模式与原型设计模式?

享元设计模式(Flyweight)和原型设计模式(Prototype)都是创建相关的设计模式,但它们解决的问题、使用方式和结构完全不同。下面是它们的详细对比:

6.1.1. 🆚 Flyweight vs. Prototype

维度

享元模式(Flyweight)

原型模式(Prototype)

💡 设计模式类型

结构型模式

创建型模式

🎯 目的

通过共享对象来减少内存占用和对象数量

通过复制已有对象来创建新对象,避免 new 开销

📦 关注点

对象复用与共享,分离内部状态与外部状态

快速创建新对象(特别是复杂结构)

🧠 实现机制

享元工厂维护共享对象池,通过传入外部状态来复用对象

使用 clone()

方法或拷贝构造函数复制现有对象

📂 状态管理

内部状态共享,外部状态由使用方维护

完整复制所有状态(深拷贝/浅拷贝)

📈 适合场景

- 大量重复对象,如文字编辑器中的字符对象
- 数据缓存池
- 游戏中的粒子/地图图块共享

- 克隆原型对象
- 对象构造成本高(如树状结构)

🧩 示例

String Pool、Integer.valueOf、数据库连接池

原型注册器、工作流模板复制、前端组件克隆等

⚠️ 使用注意点

- 内外部状态划分要明确
- 不适合状态频繁变化对象

- 深拷贝需注意引用类型对象

6.1.2. ✅ 简单总结

  • 享元模式 = 节省内存、共享对象:适用于大量对象重复的场景。
  • 原型模式 = 快速复制、提升性能:适用于快速创建复杂对象的场景。

6.1.3. 📌 举个类比:

类比

描述

享元

比如一个图书馆的“图书”是共享的,用户借用的是引用,图书本身不复制。

原型

比如一个表格模板,每次创建新文档都是复制模板,然后修改。

博文参考

  • 5. 享元模式 — Graphic Design Patterns
  • 享元设计模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

互联网大厂Java求职面试:云原生微服务架构设计与AI大模型集成实战

互联网大厂Java求职面试&#xff1a;云原生微服务架构设计与AI大模型集成实战 面试场景设定 人物设定&#xff1a; 李明&#xff08;技术总监&#xff09;&#xff1a;拥有15年分布式系统架构经验&#xff0c;主导过多个亿级用户系统的重构&#xff0c;对云原生和AI融合有深…

nginx+tomcat动静分离、负载均衡

一、理论 nginx用于处理静态页面以及做调度器&#xff0c;tomcat用于处理动态页面 lvs&#xff08;四层&#xff09; 轮询&#xff08;rr&#xff09; 加权轮询&#xff08;wrr&#xff09; 最小连接&#xff08;lc&#xff09; 加权最小连接&#xff08;wlc&#xff09; ngi…

什么是AI芯片?

首先&#xff0c;我们要了解一下&#xff1a;什么是芯片&#xff1f;芯片的本质就是在半导体衬底上制作能实现一系列特定功能的集成电路。 其次&#xff0c;来看一下AI的概念。AI是研究如何使计算机能够模拟和执行人类智能任务的科学和技术领域&#xff0c;致力于开发能够感知…

PostgreSQL数据库配置SSL操作说明书

背景&#xff1a; 因为postgresql或者mysql目前通过docker安装&#xff0c;只需要输入主机IP、用户名、密码即可访问成功&#xff0c;这样其实是不安全的&#xff0c;可能会通过一些手段获取到用户名密码导致数据被窃取。而ES、kafka等也是通过用户名/密码方式连接&#xff0c;…

k8s更新证书

[rootk8s-master01 ~]# sudo kubeadm certs renew all [renew] Reading configuration from the cluster… [renew] FYI: You can look at this config file with ‘kubectl -n kube-system get cm kubeadm-config -o yaml’ certificate embedded in the kubeconfig file for…

正点原子lwIP协议的学习笔记

正点原子lwIP协议的学习笔记 正点原子lwIP学习笔记——lwIP入门 正点原子lwIP学习笔记——MAC简介 正点原子lwIP学习笔记——PHY芯片简介 正点原子lwIP学习笔记——以太网DMA描述符 正点原子lwIP学习笔记——裸机移植lwIP 正点原子lwIP学习笔记——裸机lwIP启动流程 正点…

MongoTemplate常用api学习

本文只介绍常用的api&#xff0c;尽量以最简单的形式学会mongoTemplate基础api的使用 一、新增 主要包含三个api&#xff1a;insert&#xff08;一个或遍历插多个&#xff09;、insertAll&#xff08;批量多个&#xff09;、save&#xff08;插入或更新&#xff09; //这里简…

006网上订餐系统技术解析:打造高效便捷的餐饮服务平台

网上订餐系统技术解析&#xff1a;打造高效便捷的餐饮服务平台 在数字化生活方式普及的当下&#xff0c;网上订餐系统成为连接餐饮商家与消费者的重要桥梁。该系统以菜品分类、订单管理等模块为核心&#xff0c;通过前台展示与后台录入的分工协作&#xff0c;为管理员和会员提…

网络攻防技术五:网络扫描技术

文章目录 一、网络扫描的基础概念二、主机发现三、端口扫描1、端口号2、端口扫描技术3、端口扫描隐秘策略 四、操作系统识别五、漏洞扫描六、简答题1. 主机扫描的目的是什么&#xff1f;请简述主机扫描方法。2. 端口扫描的目的是什么&#xff1f;请简述端口扫描方法及扫描策略。…

生成JavaDoc文档

生成 JavaDoc 文档 1、快速生成 文档 注解 2、常见的文档注解 3、脚本生成 doc 文档 4、IDEA工具栏生成 doc 文档 第一章 快速入门 第01节 使用插件 在插件工具当中&#xff0c;找到插件 javaDoc 使用方式&#xff0c;在代码区域&#xff0c;直接点击右键。选择 第02节 常用注…

大数据-276 Spark MLib - 基础介绍 机器学习算法 Bagging和Boosting区别 GBDT梯度提升树

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大模型篇章已经开始&#xff01; 目前已经更新到了第 22 篇&#xff1a;大语言模型 22 - MCP 自动操作 FigmaCursor 自动设计原型 Java篇开…

【HarmonyOS 5】如何优化 Harmony-Cordova 应用的性能?

以下是针对 ‌Harmony-Cordova 应用性能优化‌的完整方案&#xff0c;结合鸿蒙原生特性和Cordova框架优化策略&#xff1a; ‌⚡一、渲染性能优化‌ ‌减少布局嵌套层级‌ 使用扁平化布局&#xff08;如 Grid、GridRow&#xff09;替代多层 Column/Row 嵌套&#xff0c;避免冗…

数据库管理-第332期 大数据已死,那什么当立?(20250602)

数据库管理332期 2025-06-02 数据库管理-第332期 大数据已死&#xff0c;那什么当立&#xff1f;&#xff08;20250602&#xff09;1 概念还是技术2 必然的大数据量3 离线到实时4 未来总结 数据库管理-第332期 大数据已死&#xff0c;那什么当立&#xff1f;&#xff08;202506…

相机--RGBD相机

教程 分类原理和标定 原理 视频总结 双目相机和RGBD相机原理 作用 RGBD相机RGB相机深度&#xff1b; RGB-D相机同时获取两种核心数据&#xff1a;RGB彩色图像和深度图像&#xff08;Depth Image&#xff09;。 1. RGB彩色图像 数据格式&#xff1a; 标准三通道矩阵&#…

神经符号集成-三篇综述

讲解三篇神经符号集成的综述&#xff0c;这些综述没有针对推荐系统的&#xff0c;所以大致过一下&#xff0c;下一篇帖子会介绍针对KG的两篇综述。综述1关注的是系统集成和数据流的宏观模式“是什么”&#xff1b;综述3关注的是与人类理解直接相关的中间过程和决策逻辑的透明度…

window/linux ollama部署模型

模型部署 模型下载表: deepseek-r1 win安装ollama 注意去官网下载ollama,这个win和linux差别不大,win下载exe linux安装ollama 采用docker方式进行安装: OLLAMA_HOST=0.0.0.0:11434 \ docker run -d \--gpus all \-p 11434:11434 \--name ollama \-v ollama:/root/.ol…

计算A图片所有颜色占B图片红色区域的百分比

import cv2 import numpy as npdef calculate_overlap_percentage(a_image_path, b_image_path):# 读取A组和B组图像a_image cv2.imread(a_image_path)b_image cv2.imread(b_image_path)# 将图像从BGR转为HSV色彩空间&#xff0c;便于颜色筛选a_hsv cv2.cvtColor(a_image, c…

每日算法 -【Swift 算法】盛最多水的容器

盛最多水的容器&#xff1a;Swift 解法与思路分析 &#x1f4cc; 问题描述 给定一个长度为 n 的整数数组 height&#xff0c;每个元素表示在横坐标 i 处的一条垂直线段的高度。任意两条线段和 x 轴构成一个容器&#xff0c;该容器可以装水&#xff0c;水量的大小由较短的那条…

云原生安全基础:Linux 文件权限管理详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 在云原生环境中&#xff0c;Linux 文件权限管理是保障系统安全的核心技能之一。无论是容器化应用、微服务架构还是基础设施即代码&#xff08;IaC&#xf…

TypeScript 中的字面量类型(Literal Types)

在 TypeScript 中&#xff0c;字面量类型&#xff08;Literal Types&#xff09;是一种特殊的类型&#xff0c;它允许你将变量的类型限制为某个具体的值&#xff08;如特定的字符串、数字或布尔值&#xff09;&#xff0c;而不仅仅是宽泛的类型&#xff08;如 string、number&a…