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也正确,例程无法完成综合:这种情…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
