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

C++设计模式之结构型模式:享元模式(Flyweight)

享元模式(Flyweight)是结构型设计模式的一种,它通过共享细粒度对象来减少内存占用和提高性能,特别适合处理大量相似对象的场景(如文字处理中的字符、游戏中的粒子效果)。

一、核心思想与角色

享元模式的核心是“区分内部状态与外部状态,共享内部状态”,从而减少对象数量。其核心角色如下:

角色名称核心职责
抽象享元(Flyweight)定义享元对象的接口,声明接收外部状态的方法。
具体享元(ConcreteFlyweight)实现抽象享元接口,存储内部状态(可共享),不存储外部状态(需从客户端传入)。
非享元(UnsharedConcreteFlyweight)不参与共享的享元子类,通常包含不能共享的外部状态。
享元工厂(FlyweightFactory)管理享元对象池,负责创建和复用享元对象,确保相同内部状态的对象只存在一个实例。
客户端(Client)维护享元对象的外部状态,通过工厂获取享元对象并使用。

核心思想:将对象的状态分为可共享的内部状态(如字符的字体、大小)和不可共享的外部状态(如字符的位置),通过共享内部状态减少对象数量,外部状态由客户端在使用时传入。

二、实现示例(文字处理系统)

假设我们需要实现一个文字处理系统,文档中包含大量字符(如字母、数字),相同字符(如多个’A’)具有相同的字体和大小(内部状态),但位置不同(外部状态)。使用享元模式可大幅减少字符对象数量:

#include <iostream>
#include <string>
#include <map>
#include <vector>// 外部状态:字符位置(不共享)
struct Position {int x; // 横坐标int y; // 纵坐标Position(int x_, int y_) : x(x_), y(y_) {}
};// 1. 抽象享元:字符
class CharacterFlyweight {
public:// 纯虚方法:显示字符(需要外部状态position)virtual void display(const Position& position) const = 0;virtual ~CharacterFlyweight() = default;
};// 2. 具体享元:具体字符(如'A'、'B')
class ConcreteCharacter : public CharacterFlyweight {
private:char symbol;       // 字符符号(内部状态)std::string font;  // 字体(内部状态)int size;          // 大小(内部状态)public:// 构造函数:初始化内部状态(可共享)ConcreteCharacter(char s, const std::string& f, int sz): symbol(s), font(f), size(sz) {}// 显示字符:结合外部状态(位置)void display(const Position& position) const override {std::cout << "字符 '" << symbol << "'(字体:" << font << ",大小:" << size << ")位于 (" << position.x << "," << position.y << ")" << std::endl;}
};// 3. 享元工厂:字符工厂
class CharacterFactory {
private:// 享元池:存储共享的字符对象(key为"符号+字体+大小"的组合)std::map<std::string, CharacterFlyweight*> flyweights;public:// 获取享元对象:存在则复用,不存在则创建CharacterFlyweight* getCharacter(char symbol, const std::string& font, int size) {// 生成唯一键(内部状态组合)std::string key = std::string(1, symbol) + "|" + font + "|" + std::to_string(size);// 检查池中是否存在if (flyweights.find(key) == flyweights.end()) {// 不存在则创建新对象并加入池flyweights[key] = new ConcreteCharacter(symbol, font, size);std::cout << "创建新享元:" << key << std::endl;} else {std::cout << "复用享元:" << key << std::endl;}return flyweights[key];}// 析构函数:释放所有享元对象~CharacterFactory() {for (auto& pair : flyweights) {delete pair.second;}}
};// 客户端代码:文档处理
int main() {// 创建享元工厂CharacterFactory factory;// 文档内容:多个相同字符(共享内部状态)std::vector<std::pair<CharacterFlyweight*, Position>> document;// 添加字符到文档(相同"符号+字体+大小"会复用享元)document.emplace_back(factory.getCharacter('A', "Arial", 12), Position(10, 20));document.emplace_back(factory.getCharacter('A', "Arial", 12),  // 复用上面的'A'Position(30, 20));document.emplace_back(factory.getCharacter('B', "Arial", 12),  // 新享元Position(50, 20));document.emplace_back(factory.getCharacter('A', "Times", 14),  // 新享元(字体和大小不同)Position(10, 40));document.emplace_back(factory.getCharacter('A', "Times", 14),  // 复用上面的'A'Position(30, 40));// 显示文档中所有字符(结合外部状态)std::cout << "\n=== 文档内容 ===" << std::endl;for (const auto& item : document) {item.first->display(item.second);}return 0;
}

三、代码解析

  1. 内部状态与外部状态

    • 内部状态ConcreteCharacter中的symbol(字符)、font(字体)、size(大小),这些属性相同的字符可以共享。
    • 外部状态Position中的xy(位置),每个字符的位置不同,无法共享,由客户端在使用时传入。
  2. 抽象享元(CharacterFlyweight)
    定义了display()方法,参数为外部状态Position,确保所有享元对象都能接收外部状态。

  3. 具体享元(ConcreteCharacter)
    存储内部状态,实现display()方法,将内部状态与传入的外部状态结合使用(显示字符及其位置)。

  4. 享元工厂(CharacterFactory)

    • 维护一个flyweights池(map容器),键为内部状态的组合(符号|字体|大小),值为对应的享元对象。
    • getCharacter()方法:当请求的字符已存在时复用,不存在时创建新对象并加入池,确保相同内部状态的对象只存在一个。
  5. 客户端使用
    客户端通过工厂获取享元对象,维护外部状态(位置),并调用display()方法时传入外部状态,实现字符的显示。

四、核心优势与适用场景

优势
  1. 减少内存占用:通过共享相同内部状态的对象,大幅减少系统中的对象数量(如示例中5个字符实际只创建3个享元对象)。
  2. 提高性能:减少对象创建和销毁的开销,尤其适合大量相似对象的场景。
  3. 分离状态:明确区分内部状态(可共享)和外部状态(不可共享),使系统设计更清晰。
适用场景
  1. 存在大量相似对象:如文字处理中的字符、游戏中的粒子(火焰、雨滴)、电商系统中的商品规格。
  2. 对象的大部分状态可共享:内部状态占比高,外部状态占比低,共享收益明显。
  3. 需要缓存对象复用:通过工厂池管理对象,避免重复创建。

五、与其他模式的区别

模式核心差异点
享元模式共享相似对象的内部状态,减少内存占用,关注“对象复用”。
单例模式确保一个类只有一个实例,关注“唯一性”,不涉及状态共享。
原型模式通过克隆创建对象,关注“快速复制”,不强调状态共享。
工厂模式负责对象创建,不涉及对象复用,享元模式中的工厂是特殊的“缓存工厂”。

六、实践建议

  1. 明确状态划分:清晰区分内部状态(不变、可共享)和外部状态(可变、不可共享),这是享元模式的关键。
  2. 工厂池设计:享元工厂应高效管理对象池(如用hash map存储),确保快速查找和复用。
  3. 外部状态处理:外部状态由客户端管理,避免享元对象存储外部状态导致无法共享。
  4. 线程安全:多线程环境下,需为享元工厂的get方法添加同步机制,避免并发创建重复对象。

享元模式的核心价值在于“通过共享减少对象数量”,当系统中存在大量相似对象且内存占用过高时,它能显著优化资源使用。其关键是合理划分内部状态和外部状态,通过工厂实现对象复用,是处理“大量细粒度对象”场景的最佳设计模式。

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

相关文章:

  • STM32 智能垃圾桶项目笔记(一):超声波模块(HC-SR04)原理与驱动实现
  • 全文 -- Vortex: Extending the RISC-V ISA for GPGPU and 3D-Graphics Research
  • 设计网站推荐理由公司网站备案电话
  • 事件驱动与CDS:基于FHIR R5 Subscriptions与Bulk Data的再考察(下)
  • Tigshop开源商城系统 Java v5.2.2 / PHP v5.1.6版本正式发布(ES搜索上新)
  • 仙游县住房和城乡建设局网站1元涨1000粉丝网站
  • 【Linux】进程概念(六):进程地址空间深度解析:虚拟地址与内存管理的奥秘
  • 网站怎么做微信登录界面wordpress restful
  • Linux下写一个简陋的shell程序
  • OpenSource - 异构数据库数据与结构同步工具dbswitch
  • 首次披露潮玩成长性,量子之歌敲响新财年重估的钟声
  • jdk21 list中筛选出符合条件的list
  • Session共享问题
  • 3. Ollama 安装,流式输出,多模态,思考模型
  • Go基础:常用数学函数处理(主要是math包rand包的处理)
  • 做彩票网站被捉将受到什么惩罚北京网站建设公司制作网站
  • 沈阳小程序建设兰州seo优化
  • 低疲劳高响应!硬件软件协同:明基 RD280U 赋能创作开发,解锁新工位高效工作氛围
  • Apache Log4j2 lookup JNDI 注入漏洞(CVE-2021-44228)
  • wpf之 Popup
  • @xyflow/react:构建交互式节点流程图的完整指南
  • LinuxC++项目开发日志——基于正倒排索引的boost搜索引擎(5——通过cpp-httplib库建立网页模块)
  • 消息队列Apache Kafka教程
  • Hadess入门到实战(3) - 如何管理Npm制品
  • 【AI算法工程师的一些子路】音频大模型从入门到精通:算法工程师的成长之路
  • 透明的多级并发(行) 方式
  • 音乐网站还可以做做seo网站的公司哪家好
  • 【python3】Streamlit快速构建前端页面
  • ​FAQ: 如何在 WPF 项目中强制指定统一输出目录并确保 VS 调试正常?
  • mysql数据库学习之数据查询进阶操作(三)