C++设计模式---享元模式
1、介绍
原理:
享元模式是一种主要用于减少创建对象的数量,以减少内存占用和提高性能的结构型设计模式。它通过共享多个对象所共有的相同状态,使得在有限的内存容量中能够载入更多的对象。具体来说,享元模式将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State)。
内部状态:是存储在享元对象内部的信息,它是不可变的,可以在多个享元对象之间共享。
外部状态:是依赖于享元对象上下文的信息,它随着享元对象的每一次使用而变化,通常作为参数传递给享元对象的方法。
享元模式的核心在于享元工厂类,它负责创建和管理享元对象,并提供对外访问的接口。当客户端请求一个享元对象时,享元工厂会首先检查是否已存在具有相同内部状态的享元对象,如果存在则直接返回该对象,否则创建一个新的享元对象并存储起来。通过这种方式,享元模式实现了对象的复用,减少了对象的创建和销毁次数,从而提高了系统的性能。
使用场景:
享元模式通常用于以下场景:
(1)系统中存在大量相似对象:当系统中需要创建大量细粒度的对象,并且这些对象具有相同的内部状态时,可以使用享元模式来减少对象的数量,节省内存空间。
(2)对象的创建和销毁代价较大:如果对象的创建和销毁需要消耗大量的计算资源或时间,使用享元模式可以通过复用已有的对象来避免不必要的创建和销毁操作,从而提高系统的性能。
(3)系统不依赖于对象身份:在享元模式中,由于多个客户端可能共享同一个享元对象,因此系统不应该依赖于对象的身份(即内存地址)来区分不同的对象。相反,系统应该通过对象的内部状态和外部状态来区分对象。
优缺点:
优点:
(1)减少了对象的数量,降低了内存占用。
(2)提高了系统的性能,减少了对象的创建和销毁次数。
(3)支持可变状态和不可变状态,提高了对象的灵活性。
缺点:
(1)增加了系统的复杂性,需要额外的工厂类来管理享元对象的创建和共享。
(2)可能引入线程安全问题,需要对共享对象的并发访问进行合理的同步控制。
总结:
C++享元模式通过共享对象的内部状态来减少对象的数量,从而节省内存空间并提高系统的性能。它适用于处理大量相似对象或对象创建和销毁代价较大的场景。然而,引入享元模式也会增加系统的复杂性,并可能引入线程安全问题。因此,在使用享元模式时需要根据具体情况权衡利弊,合理设计和管理共享对象。
2、示例
示例模拟了一个咖啡店订单系统,其中咖啡口味被视为享元对象,可以被多个订单共享。
#include <iostream>
#include <map>
#include <vector>
#include <memory>// 抽象享元接口
class CoffeeFlavor {
public:virtual ~CoffeeFlavor() {}virtual void serve() const = 0;
};// 具体享元类
class ConcreteCoffeeFlavor : public CoffeeFlavor {
public:explicit ConcreteCoffeeFlavor(const std::string& flavorName): flavorName_(flavorName) {}void serve() const override {std::cout << "Serving coffee flavor: " << flavorName_ << std::endl;}private:std::string flavorName_;
};// 享元工厂
class CoffeeFlavorFactory {
private:std::map<std::string, std::shared_ptr<CoffeeFlavor>> flavors;public:std::shared_ptr<CoffeeFlavor> getOrder(const std::string& flavor) {if (flavors.find(flavor) == flavors.end()) {flavors[flavor] = std::make_shared<ConcreteCoffeeFlavor>(flavor);}return flavors[flavor];}
};int main() {CoffeeFlavorFactory factory;// 创建几个不同的订单,但某些口味会被共享std::vector<std::string> orders = {"Espresso", "Latte", "Espresso", "Cappuccino", "Espresso"};for (const auto& order : orders) {auto flavor = factory.getOrder(order);flavor->serve();}return 0;
}
在这个例子中:
(1)CoffeeFlavor 是抽象享元类,定义了所有咖啡口味的基本行为,即服务(serve)咖啡。
(2)ConcreteCoffeeFlavor 是具体享元类,实现了咖啡口味的具体行为,并存储了口味名称这一内部状态。
(3)CoffeeFlavorFactory 是享元工厂,它维护了一个储存所有咖啡口味实例的映射表。当客户请求某个口味的咖啡时,工厂会检查是否已经创建过该口味的实例,如果没有则创建一个新的实例,否则返回已有的实例,从而实现了口味的共享。
运行此代码,可以看到虽然订单列表中有多个"Espresso",但在打印结果中只会看到一次"Serving coffee flavor: Espresso",这是因为享元模式让多次请求相同口味的咖啡共享了同一个实例。
实际上,上述咖啡口味享元模式的案例并没有体现出享元模式对外部状态的处理。在一个更全面的示例中,我们可能还会遇到咖啡订单具有外部状态,如客户的偏好(加糖、加奶)、杯子大小等。这时可以对示例稍作修改,将外部状态从享元对象中分离出来:
#include <iostream>
#include <map>
#include <vector>
#include <memory>// 抽象享元接口
class CoffeeFlavor {
public:virtual ~CoffeeFlavor() {}virtual void serve(const std::string& extras, const std::string& size) const = 0;
};// 具体享元类
class ConcreteCoffeeFlavor : public CoffeeFlavor {
public:explicit ConcreteCoffeeFlavor(const std::string& flavorName): flavorName_(flavorName) {}void serve(const std::string& extras, const std::string& size) const override {std::cout << "Serving " << size << " cup of " << flavorName_<< " with extras: " << extras << std::endl;}private:std::string flavorName_;
};// 享元工厂
class CoffeeFlavorFactory {
private:std::map<std::string, std::shared_ptr<CoffeeFlavor>> flavors;public:std::shared_ptr<CoffeeFlavor> getOrder(const std::string& flavor) {if (flavors.find(flavor) == flavors.end()) {// 没有找到就新建一个对象flavors[flavor] = std::make_shared<ConcreteCoffeeFlavor>(flavor);}return flavors[flavor];}
};// 订单类,包含外部状态
class CoffeeOrder {
public:CoffeeOrder(std::shared_ptr<CoffeeFlavor> flavor, const std::string& extras, const std::string& size): flavor_(flavor), extras_(extras), size_(size) {}void serve() const {flavor_->serve(extras_, size_);}private:std::shared_ptr<CoffeeFlavor> flavor_;std::string extras_;std::string size_;
};int main() {CoffeeFlavorFactory factory;// 创建几个不同的订单,但某些口味会被共享std::vector<CoffeeOrder> orders = {{factory.getOrder("Espresso"), "no sugar", "small"},{factory.getOrder("Latte"), "extra foam", "medium"},{factory.getOrder("Espresso"), "double sugar", "large"},{factory.getOrder("Cappuccino"), "cinnamon", "medium"},{factory.getOrder("Espresso"), "single sugar", "small"}};for (const auto& order : orders) {order.serve();}return 0;
}
结果:
Serving small cup of Espresso with extras: no sugar
Serving medium cup of Latte with extras: extra foam
Serving large cup of Espresso with extras: double sugar
Serving medium cup of Cappuccino with extras: cinnamon
Serving small cup of Espresso with extras: single sugar
3、参考
C++设计模式之——享元模式详解和代码案例_c++享元模式-CSDN博客
相关文章:
C++设计模式---享元模式
1、介绍 原理: 享元模式是一种主要用于减少创建对象的数量,以减少内存占用和提高性能的结构型设计模式。它通过共享多个对象所共有的相同状态,使得在有限的内存容量中能够载入更多的对象。具体来说,享元模式将对象的状态分为内部…...
智慧园区大数据云平台建设方案(Word原件)
第一章 项目建设背景及现状 第二章 园区创新发展趋势 第三章 工业园区大数据存在的问题 第四章 智慧工业园区大数据建设目的 第五章 智慧园区总体构架 第六章 系统核心组件 第七章 智慧工业园区大数据平台规划设计 获取方式:本文末个人名片直接获取。 软件资料清单…...
【学习】如何利用Python技术进行软件测试相关工作
Python是一种广泛使用的高级编程语言,它因其简洁的语法、强大的库支持和跨平台特性而受到开发者的喜爱。在软件测试领域,Python同样发挥着重要作用,它可以帮助测试人员编写自动化测试脚本、进行接口测试、性能测试、以及处理测试数据等。以下…...
Qt:3.项目创建、对象树、乱码问题、Qt命名规则
目录 1.创建项目: 2.Qt可以支持两套基础类: 3.节点的父子关系和对象树: 4.QLabel类: 5.乱码问题: 6.Qt命名规则: 1.创建项目: qt的项目中有一个以.ui为后缀的文件,他本质是一个…...
C# 入门—实现 Hello, World!
目录 一、.net 平台 二、.net 都能干什么? 三、.net 两种交互模式 四、使用 VS Code 开发 C# 程序 五、实现 Hello, World! 一、.net 平台 下载 .NET(Linux、macOS 和 Windows) (microsoft.com) .NET 简介 - .NET | Microsoft Learn C# :一种编程语言,可以开…...
【项目实训】前端页面初探索(前期探索)
前期,由于没有确定页面展示形式,于是进行了很多探索 首先安装element-ui 导入elemnt-plus 添加use: 设置一个全局样式 编写导航栏 <el-menu:default-active"activeIndex"class"el-menu-demo"background-color"#95d475&quo…...
机器人控制系列教程之动力学建模(2)
接昨天的推文:https://editor.csdn.net/md/?articleId139991958 ,动力学的求解通常是个相对比较复杂的过程,但现在基本上不用人工来推算求解各种公式和求解过程了,大家只需要知道其中的步骤即可,现代对于动力学问题的…...
Golang | Leetcode Golang题解之第200题岛屿数量
题目: 题解: func numIslands(grid [][]byte) int {res : 0for i : 0; i < len(grid); i {for j : 0; j < len(grid[i]); j {if grid[i][j] 1 {resdfs(grid, i, j)}}}return res }func dfs(grid [][]byte, r, c int) {h, w : len(grid), len(gri…...
Linux系统启动流程
init程序类型: ①、SysV:init,centos 5之前,配置文件/etc/init.d/ ②、Upstart: init,centos 6,配置文件/etc/init.d/ /etc/init/ ③、Systemd:Systemd,centos 7,配置文件/usr/li…...
Vue 学习之 axios
目录 执行安装命令:npm install axios 使用的时候导入 axios以data,params,headers传参方式的区别 axios封装 是一个基于 promise 的 网络请求库,作用于浏览器和 node.js 中。使用Axios可以在前端项目中发送各种方式的HTTP请求…...
Python学习笔记17 -- 猜数字小游戏2
目录 一、功能函数 1、说明函数 -- 对游戏玩法及设置进行说明 2、答案函数 -- 生成答案 3、猜测函数 -- 让玩家进行猜测 4、对照函数 -- 将答案和猜测进行对照 4.1 A函数 4.2 B函数 5、结果函数 -- 判断得到结果或继续猜测 6、时间函数 -- 判断一局游戏所用时间 7、打…...
【系统架构设计师】七、信息安全技术基础知识(信息安全的概念|信息安全系统的组成框架|信息加解密技术)
目录 一、信息安全的概念 1.1 信息安全的基本要素和范围 1.2 信息存储安全 1.3 网络安全 二、信息安全系统的组成框架 2.1 技术体系 2.2 组织机构体系 2.3 管理体系 三、 信息加解密技术 3.1 数据加密 3.2 对称加密技术 3.3 非对称加密算法 3.4 数字信封 3.5 信…...
CMMM Plus+ Calculus Update 超级游戏大作 游戏说明
资源链接 Scratch超级生命模拟游戏:CMMMPlusCalculusUpdate.sb3资源-CSDN文库 关卡编辑器 ◽️使用 WASD 移动视图。 ◽️LMB 放置单元格。 ◽️Space LMB 删除单元格。Ctrl Space LMB 删除所有相同类型的单元格。 ◽️Q / E 旋转单元格。 ◽️Z / X 在单元格类…...
Java OA系统任务协作模块
以下是一篇关于构建高效且功能丰富的OA系统任务协作模块的博客文章,采用了Spring Boot、Spring Data JPA和React等主流技术。文章不仅展示了项目的基本实现,还介绍了如何优化代码和增加新的功能,以提升系统的性能和用户体验。 --- ## 构建高…...
深入解析Maven常用命令
目录 什么是 MavenMaven 的安装与配置Maven 项目结构Maven 常用命令 mvn cleanmvn compilemvn testmvn packagemvn installmvn deploymvn sitemvn dependencymvn help 总结 什么是 Maven Maven 是由 Apache 软件基金会开发的一个项目管理和构建工具。它基于项目对象模型&…...
【Docker】镜像
目录 1. 镜像拉取 2. 镜像查询 3. 镜像导出 4. 镜像上传 5. 镜像打标签 6. 镜像上推 7. 镜像删除 8. 镜像运行及修改 8.1 在registry 节点运行 mariadb 镜像,将宿主机 13306 端口作为容器3306 端口映射 8.2 查看容器ID 8.3 进入容器 8.4 创建数据库xd_d…...
力扣最新详解5道题:两数之和三数之和四数之和
目录 一、查找总价格为目标值的两个商品 题目 题解 方法一:暴力枚举 方法二:对撞指针 二、两数之和 题目 题解 方法一:暴力枚举 方法二:哈希表法 三、三数之和 题目 题解 方法一:排序暴力枚举set去重 …...
通讯:单片机串口和电脑通讯
目录 1.串口输出数据到电脑 硬件部分 串口输出数据到电脑的软件软件部分: 相关问题: 2.单片机串口--485--485转USB--电脑 串口,芯片,转换器,设备之间的通讯的接线,都是要TX--RX, RX--TX 交叉连接。 单…...
ubuntu22.04 设置双屏
一 概述 最近把ubuntu18.04 升级到 22.04 双屏显示出来问题,在此记录下解决问题方案。二 解决方案 1 使用命令查看能检测到显示器 xrandr根据输出的信息,我们可以知道 HDMI-0 与 DP-0 是connected 。检测到两个显示器 2 设置输出显示器分辨率 由于我…...
【FPGA-常见问题及解决方案】
1、VIVADO的License无法加载:license文件必须在英文路径; 2、例程代码路径不能过长,也不允许有中文路径!!! 3、明明加载了license,license也正确,例程无法完成综合:这种情…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
