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也正确,例程无法完成综合:这种情…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
