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

享元模式C++

享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享技术有效地支持大量细粒度对象的复用,从而减少内存消耗和对象创建开销。这种模式特别适合处理大量相似对象,这些对象中包含可共享的"内部状态"和不可共享的"外部状态"。

享元模式的核心角色

  1. Flyweight(享元接口):定义享元对象的接口,接受并作用于外部状态
  2. ConcreteFlyweight(具体享元):实现享元接口,存储内部状态
  3. FlyweightFactory(享元工厂):管理享元对象,负责创建和复用享元
  4. Client(客户端):维护享元对象的外部状态,通过工厂获取享元

C++实现示例

以下以"文字处理软件"为例实现享元模式,文档中的字符具有可共享的内部状态(字符值、字体等)和不可共享的外部状态(位置、颜色等):

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <vector>// 享元接口:字符享元
class CharacterFlyweight {
public:virtual ~CharacterFlyweight() = default;// 绘制字符,接收外部状态virtual void draw(int x, int y, const std::string& color) const = 0;virtual char getSymbol() const = 0;
};// 具体享元:具体字符
class ConcreteCharacter : public CharacterFlyweight {
private:char symbol;       // 字符值(内部状态,可共享)std::string font;  // 字体(内部状态,可共享)int size;          // 字号(内部状态,可共享)public:ConcreteCharacter(char sym, std::string fnt, int sz): symbol(sym), font(std::move(fnt)), size(sz) {}// 绘制字符,结合外部状态(位置和颜色)void draw(int x, int y, const std::string& color) const override {std::cout << "绘制字符 '" << symbol << "' 在位置 (" << x << "," << y << "),颜色: " << color << ",字体: " << font << ",字号: " << size << std::endl;}char getSymbol() const override {return symbol;}
};// 享元工厂:字符工厂
class CharacterFactory {
private:// 缓存享元对象,键为"字符-字体-字号"的组合std::unordered_map<std::string, std::shared_ptr<CharacterFlyweight>> flyweights;// 生成缓存键std::string getKey(char symbol, const std::string& font, int size) {return std::string(1, symbol) + "|" + font + "|" + std::to_string(size);}public:// 获取或创建享元对象std::shared_ptr<CharacterFlyweight> getCharacter(char symbol, const std::string& font, int size) {std::string key = getKey(symbol, font, size);// 如果不存在,则创建并缓存if (flyweights.find(key) == flyweights.end()) {flyweights[key] = std::make_shared<ConcreteCharacter>(symbol, font, size);std::cout << "创建新的享元对象: " << key << std::endl;} else {std::cout << "复用享元对象: " << key << std::endl;}return flyweights[key];}// 获取缓存的享元数量size_t getFlyweightCount() const {return flyweights.size();}
};// 字符上下文:包含外部状态
struct CharacterContext {std::shared_ptr<CharacterFlyweight> flyweight;int x;          // 位置X(外部状态)int y;          // 位置Y(外部状态)std::string color;  // 颜色(外部状态)void draw() const {flyweight->draw(x, y, color);}
};// 文档类:使用享元的客户端
class Document {
private:CharacterFactory factory;std::vector<CharacterContext> characters;public:void addCharacter(char symbol, const std::string& font, int size,int x, int y, const std::string& color) {// 从工厂获取享元auto flyweight = factory.getCharacter(symbol, font, size);// 存储外部状态和享元引用characters.push_back({flyweight, x, y, color});}void render() const {std::cout << "\n开始渲染文档..." << std::endl;for (const auto& ctx : characters) {ctx.draw();}std::cout << "文档渲染完成" << std::endl;}void showStats() const {std::cout << "\n文档统计: " << std::endl;std::cout << "总字符数: " << characters.size() << std::endl;std::cout << "享元对象数: " << factory.getFlyweightCount() << std::endl;}
};int main() {Document doc;// 添加文档内容(大量重复字符)doc.addCharacter('H', "Arial", 12, 10, 20, "black");doc.addCharacter('e', "Arial", 12, 20, 20, "black");doc.addCharacter('l', "Arial", 12, 30, 20, "black");doc.addCharacter('l', "Arial", 12, 40, 20, "black");doc.addCharacter('o', "Arial", 12, 50, 20, "black");doc.addCharacter('W', "Arial", 12, 10, 40, "blue");doc.addCharacter('o', "Arial", 12, 20, 40, "blue");doc.addCharacter('r', "Arial", 12, 30, 40, "blue");doc.addCharacter('l', "Arial", 12, 40, 40, "blue");doc.addCharacter('d', "Arial", 12, 50, 40, "blue");doc.addCharacter('!', "Times New Roman", 14, 60, 30, "red");// 显示统计信息doc.showStats();// 渲染文档doc.render();return 0;
}

代码解析

  1. 状态划分

    • 内部状态:字符值(symbol)、字体(font)、字号(size),这些属性相同的字符可以共享
    • 外部状态:位置(xy)、颜色(color),这些属性因字符位置不同而变化,不可共享
  2. 核心组件

    • ConcreteCharacter:具体享元类,存储内部状态,实现draw()方法时结合外部状态完成绘制
    • CharacterFactory:享元工厂,通过键值(字符-字体-字号组合)缓存和复用享元对象
    • CharacterContext:存储外部状态和对应的享元引用,将内部状态和外部状态结合使用
  3. 优化效果:文档中大量重复的字符(如示例中的多个’l’和’o’)只需要创建一个享元对象,显著减少了对象数量(示例中11个字符仅创建8个享元)。

享元模式的优缺点

优点

  • 大幅减少对象数量,降低内存消耗
  • 提高系统性能,减少对象创建和销毁的开销
  • 将内部状态与外部状态分离,便于管理和复用

缺点

  • 增加了系统复杂度,需要分离内部状态和外部状态
  • 外部状态的管理可能带来额外开销
  • 可能导致线程安全问题(需妥善处理共享的内部状态)

适用场景

  • 当系统中存在大量相似对象,且这些对象消耗大量内存时
  • 当对象的大部分状态可以外部化(即可以作为外部状态传递)时
  • 当需要缓冲池管理对象时

常见应用:

  • 文字处理软件(如Word)中的字符对象
  • 游戏中的粒子效果(共享粒子类型,外部状态为位置、速度等)
  • 数据库连接池(共享连接对象,外部状态为用户会话)
  • 缓存系统(如图片缓存、数据缓存)

享元模式的关键是合理划分内部状态和外部状态:内部状态是不变的、可共享的,外部状态是可变的、依赖于上下文的。通过这种划分,实现了对象的高效复用。

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

相关文章:

  • 基于深度学习的零件缺陷识别方法研究(LW+源码+讲解+部署)
  • 力扣hot100 | 普通数组 | 53. 最大子数组和、56. 合并区间、189. 轮转数组、238. 除自身以外数组的乘积、41. 缺失的第一个正数
  • 什么才是真正的白盒测试?
  • 专题三_二分_x 的平方根
  • JavaScript 解析 Modbus 响应数据的实现方法
  • 记录处理:Caused by: java.lang.UnsatisfiedLinkError
  • MARCONet++ 攻克中文文本图像超分难题
  • 疯狂星期四文案网第40天运营日记
  • Web 开发 15
  • Transformer实战(11)——从零开始构建GPT模型
  • required a bean of type ‘com.example.dao.StudentDao‘ that could not be found
  • (Arxiv-2025)Stand-In:一种轻量化、即插即用的身份控制方法用于视频生成
  • All Document Reader:一站式文档阅读解决方案
  • LT6911GXD,HD-DVI2.1/DP1.4a/Type-C 转 Dual-port MIPI/LVDS with Audio 带音频
  • 【C++】缺省参数
  • Vue3中的ref与reactive全面解析:如何正确选择响应式声明方式
  • 采购招标周期从2月缩至3周?8Manage招标系统实战案例分享
  • 社区物业HCommunity本地部署二开与使用
  • 我的学习认知、高效方法与知识积累笔记
  • JAVA 关键字
  • Redis 官方提供免费的 30 MB 云数据库
  • 【机器人】人形机器人“百机大战”:2025年产业革命的烽火与技术前沿
  • Linux线程——基于生产者消费者模型的线程同步互斥应用
  • Scikit-learn (sklearn) 库详细介绍
  • 体彩排列三第2025217期号码分析
  • 蓝牙AOA定位技术在医疗行业的创新应用与发展
  • Linux 系统中~网络配置
  • 项目篇------------网页五子棋(知识预备)
  • SVA断言一二
  • 第十六届蓝桥杯青少组C++省赛[2025.8.9]第二部分编程题(4、矩阵圈层交错旋转)