当前位置: 首页 > news >正文

【设计模式】享元模式(Flyweight)大白话讲解!

享元模式(Flyweight)大白话讲解

一句话概括

就像字母积木:有限的字母积木可以拼出无数单词,不用为每个单词都造新积木
在这里插入图片描述


现实生活比喻

场景1:字母积木

  • 有限字母:26个字母积木
  • 无限组合:用这些字母可以拼出所有英文单词
  • 节省材料:不需要为每个单词都制造一套新字母

场景2:围棋棋子

  • 棋子类型:只有黑棋和白棋两种
  • 棋盘位置:361个位置可以放棋子
  • 节省资源:不需要为每个位置都创建新棋子对象

完整代码示例

场景1:文本编辑器中的字符处理

/*** 享元模式 - 文本编辑器字符处理*/
public class Main {public static void main(String[] args) {System.out.println("=== 文本编辑器字符处理 ===");// 创建字符工厂CharacterFactory factory = new CharacterFactory();// 编辑文档String document = "Hello World! Hello Design Patterns!";System.out.println("文档内容: " + document);System.out.println("文档长度: " + document.length() + " 个字符");// 处理每个字符List<TextCharacter> characters = new ArrayList<>();for (int i = 0; i < document.length(); i++) {char c = document.charAt(i);TextCharacter character = factory.getCharacter(c);characters.add(character);}// 显示字符信息System.out.println("\n=== 字符使用统计 ===");System.out.println("实际创建的字符对象数量: " + factory.getCharacterCount());System.out.println("文档中字符总数: " + characters.size());System.out.println("节省了 " + (characters.size() - factory.getCharacterCount()) + " 个对象");// 渲染文档System.out.println("\n=== 渲染文档 ===");for (int i = 0; i < characters.size(); i++) {characters.get(i).render(i, 0); // 位置作为外部状态}}
}/*** 享元接口 - 字符*/
interface TextCharacter {void render(int positionX, int positionY);  // 位置是外部状态char getSymbol();  // 获取字符符号
}/*** 具体享元 - 具体字符*/
class Character implements TextCharacter {private final char symbol;  // 内部状态 - 不变的部分private final String font;  // 内部状态 - 字体private final int size;     // 内部状态 - 字号public Character(char symbol) {this.symbol = symbol;this.font = "Arial";    // 假设所有字符使用相同字体this.size = 12;         // 假设所有字符使用相同字号}@Overridepublic void render(int positionX, int positionY) {System.out.println("字符 '" + symbol + "' 在位置 (" + positionX + ", " + positionY + ") 字体: " + font + " 字号: " + size);}@Overridepublic char getSymbol() {return symbol;}
}/*** 享元工厂 - 字符工厂*/
class CharacterFactory {private Map<Character, TextCharacter> characters = new HashMap<>();// 获取字符对象 - 核心方法public TextCharacter getCharacter(char symbol) {// 如果字符已经存在,直接返回if (characters.containsKey(symbol)) {return characters.get(symbol);}// 否则创建新字符并缓存TextCharacter character = new Character(symbol);characters.put(symbol, character);System.out.println("创建新字符对象: '" + symbol + "'");return character;}// 获取已创建的字符数量public int getCharacterCount() {return characters.size();}
}

运行结果

=== 文本编辑器字符处理 ===
文档内容: Hello World! Hello Design Patterns!
文档长度: 37 个字符
创建新字符对象: 'H'
创建新字符对象: 'e'
创建新字符对象: 'l'
创建新字符对象: 'o'
创建新字符对象: ' '
创建新字符对象: 'W'
创建新字符对象: 'r'
创建新字符对象: 'd'
创建新字符对象: '!'
创建新字符对象: 'D'
创建新字符对象: 's'
创建新字符对象: 'i'
创建新字符对象: 'g'
创建新字符对象: 'n'
创建新字符对象: 'P'
创建新字符对象: 'a'
创建新字符对象: 't'=== 字符使用统计 ===
实际创建的字符对象数量: 17
文档中字符总数: 37
节省了 20 个对象=== 渲染文档 ===
字符 'H' 在位置 (0, 0) 字体: Arial 字号: 12
字符 'e' 在位置 (1, 0) 字体: Arial 字号: 12
字符 'l' 在位置 (2, 0) 字体: Arial 字号: 12
字符 'l' 在位置 (3, 0) 字体: Arial 字号: 12
字符 'o' 在位置 (4, 0) 字体: Arial 字号: 12
字符 ' ' 在位置 (5, 0) 字体: Arial 字号: 12
字符 'W' 在位置 (6, 0) 字体: Arial 字号: 12
字符 'o' 在位置 (7, 0) 字体: Arial 字号: 12
字符 'r' 在位置 (8, 0) 字体: Arial 字号: 12
字符 'l' 在位置 (9, 0) 字体: Arial 字号: 12
字符 'd' 在位置 (10, 0) 字体: Arial 字号: 12
字符 '!' 在位置 (11, 0) 字体: Arial 字号: 12
...

场景2:围棋游戏

/*** 享元模式 - 围棋游戏*/
public class GoGame {public static void main(String[] args) {System.out.println("=== 围棋游戏 ===");ChessFactory factory = new ChessFactory();GoBoard board = new GoBoard();// 下棋board.placeChess(factory.getChess("黑棋"), 3, 4);board.placeChess(factory.getChess("白棋"), 4, 4);board.placeChess(factory.getChess("黑棋"), 5, 5);board.placeChess(factory.getChess("白棋"), 4, 5);board.placeChess(factory.getChess("黑棋"), 3, 5);// 显示棋盘board.display();// 统计信息System.out.println("\n=== 棋子使用统计 ===");System.out.println("棋盘棋子数量: " + board.getChessCount());System.out.println("实际创建的棋子对象: " + factory.getChessCount());System.out.println("节省了 " + (board.getChessCount() - factory.getChessCount()) + " 个对象");}
}/*** 享元接口 - 棋子*/
interface Chess {String getColor();void display(int x, int y);
}/*** 具体享元 - 具体棋子*/
class GoChess implements Chess {private final String color;  // 内部状态 - 颜色public GoChess(String color) {this.color = color;}@Overridepublic String getColor() {return color;}@Overridepublic void display(int x, int y) {System.out.println(color + " 落在位置 (" + x + ", " + y + ")");}
}/*** 享元工厂 - 棋子工厂*/
class ChessFactory {private Map<String, Chess> chessMap = new HashMap<>();public Chess getChess(String color) {if (chessMap.containsKey(color)) {return chessMap.get(color);}Chess chess = new GoChess(color);chessMap.put(color, chess);System.out.println("创建新棋子: " + color);return chess;}public int getChessCount() {return chessMap.size();}
}/*** 棋盘 - 管理外部状态(位置)*/
class GoBoard {private List<ChessPosition> positions = new ArrayList<>();public void placeChess(Chess chess, int x, int y) {positions.add(new ChessPosition(chess, x, y));}public void display() {System.out.println("\n=== 棋盘状态 ===");for (ChessPosition position : positions) {position.display();}}public int getChessCount() {return positions.size();}
}/*** 棋子位置 - 外部状态*/
class ChessPosition {private Chess chess;  // 享元对象private int x;        // 外部状态 - X坐标private int y;        // 外部状态 - Y坐标public ChessPosition(Chess chess, int x, int y) {this.chess = chess;this.x = x;this.y = y;}public void display() {chess.display(x, y);}
}

运行结果

=== 围棋游戏 ===
创建新棋子: 黑棋
创建新棋子: 白棋=== 棋盘状态 ===
黑棋 落在位置 (3, 4)
白棋 落在位置 (4, 4)
黑棋 落在位置 (5, 5)
白棋 落在位置 (4, 5)
黑棋 落在位置 (3, 5)=== 棋子使用统计 ===
棋盘棋子数量: 5
实际创建的棋子对象: 2
节省了 3 个对象

享元模式的核心概念

内部状态 vs 外部状态

内部状态(Intrinsic State)

  • 不变的、可共享的部分
  • 存储在享元对象内部
  • 例子:字符的符号、字体、字号;棋子的颜色

外部状态(Extrinsic State)

  • 变化的、不可共享的部分
  • 由客户端存储和传递
  • 例子:字符的位置;棋子的坐标

核心结构

Client(客户端)↓ 使用
FlyweightFactory(享元工厂)↓ 管理
Flyweight(享元接口)↑ 实现
ConcreteFlyweight(具体享元)

适用场景

适合用享元模式的场景:

  1. 大量相似对象

    // 游戏中的子弹、粒子效果
    // 文档编辑器中的字符
    // 图形绘制中的图标
    
  2. 对象大部分状态可以外部化

    // 对象只有少量内部状态不同
    // 大量状态可以从外部传入
    
  3. 内存敏感的应用

    // 移动设备应用
    // 嵌入式系统
    // 游戏引擎
    

不适合的场景:

  1. 对象状态经常变化

    // 如果外部状态太复杂,性能反而下降
    
  2. 对象数量很少

    // 如果只有几个对象,没必要用享元
    
  3. 对象差异很大

    // 如果每个对象都很独特,无法共享
    

Java中的实际应用

1. String常量池

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");System.out.println(s1 == s2); // true - 享元模式
System.out.println(s1 == s3); // false - 新对象

2. Integer缓存

Integer i1 = 127;
Integer i2 = 127;
Integer i3 = 128;
Integer i4 = 128;System.out.println(i1 == i2); // true - 享元缓存
System.out.println(i3 == i4); // false - 超出缓存范围

3. 连接池

// 数据库连接池、线程池都是享元思想
Connection conn1 = dataSource.getConnection(); // 可能返回缓存的连接
Connection conn2 = dataSource.getConnection(); // 可能返回缓存的连接

优缺点

优点:

  • 大幅减少内存使用
  • 减少对象创建开销
  • 提高性能

缺点:

  • 增加系统复杂度
  • 需要分离内部/外部状态
  • 可能引入线程安全问题

实现要点

1. 工厂模式结合

// 享元模式通常结合工厂模式使用
public class FlyweightFactory {private 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);}
}

2. 线程安全考虑

// 多线程环境下需要同步
public synchronized Flyweight getFlyweight(String key) {// ...
}

3. 内存管理

// 可能需要清理长时间不用的享元对象
public void cleanup() {// 清理策略
}

总结

享元模式就是:

  • 资源共享:相同的对象只创建一个,大家共用
  • 内外分离:不变的部分内部存储,变化的部分外部传递
  • 池化思想:像连接池、线程池一样管理对象

核心口诀:

对象大量又相似,
内存消耗成问题。
享元模式来共享,
内存性能双收益!

就像现实中的:

  • 🔤 活字印刷:有限的字模印刷无限书籍
  • ♟️ 棋盘游戏:有限的棋子玩无限局游戏
  • 🎨 颜料调色:有限的基色调出无限颜色
  • 🧱 乐高积木:有限的积木块拼出无限造型

记住:当系统中存在大量相似对象,且这些对象可以共享部分状态时,考虑使用享元模式!

http://www.dtcms.com/a/553129.html

相关文章:

  • seo网站优化知识网站设计师的工作环境
  • 浅谈onlyoffice开发全流程(一、基础介绍)
  • 金仓KRDS云数据库服务管控平台:构建高效智能的数据库运维体系
  • GMI Cloud:如何构建全球化高性能分布式推理服务?
  • Linux入门攻坚——52、drbd - Distribute Replicated Block Device,分布式复制块设备-1
  • Threat Report ATTCK Mapper(TRAM)安装与配置手册
  • java后端初始化模版
  • WebGPU深度学习前端:基于浏览器的分布式模型推理
  • 深入浅出:增强现实(AR)技术全解析
  • 【进程间通信】--- 匿名管道,命名管道
  • 温岭网站建设公司百度问问首页登录
  • SpringBoot配置Redis
  • MinHashLSH 详解:高维数据相似性搜索与去重的关键技术
  • 数据驱动的架构设计:通过Enterprise Architect实现基于UML的架构治理
  • 旅游网页设计模板图及代码平度网站整站优化外包公司
  • Git的原理与使用 -- 远程操作
  • jetson开发板自编译编译pytorch
  • Git 仓库分支删除及清理指南
  • gitlab解决传过大文件后删除导致pack过大问题
  • sourcefare零基础学习,创建第一个扫描项目(服务端Git方式)
  • SAP使用abapGit在云环境中迁移开发包
  • DGX Spark 实战解析:模型选择与效率优化全指南
  • 宁德蕉城城乡建设网站广州建站业务公司
  • 长乐福州网站建设交换神器
  • Unity之OpenXR+XR Interaction Toolkit交互过滤器Filtering
  • MATLAB 模拟台风路径和强度变化
  • 第3讲:创建并运行你的第一个Flutter应用
  • 使用LangChain+LangGraph自定义AI工作流,实现音视频字幕生成工具
  • 【Matlab】基于模型预测控制(MPC)与滚动时域估计(MHE)集成的目标点镇定研究
  • 基于MATLAB的粒子群算法(PSO)优化libsvm参数完整实现