C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
什么是享元模式?
享元模式是一个非常实用的结构型设计模式,它的主要目的是节省内存,尤其在需要创建大量相似对象时。
- 通俗解释:
想象我们在写一本书,每个字母都需要表示出来。如果每个字母都单独用对象表示,不仅创建对象的成本高,而且内存占用也很大。但是,字母其实是有限的(比如英文字母只有26个大小写字母),我们可以让所有相同的字母对象共享,只存储不同的额外信息(比如字母的字体、颜色等)。
通过这种方式,我们避免了重复创建相同的字母对象,从而节省了大量内存。这就是享元模式的核心思想:将可以共享的部分抽取出来共享,用少量对象完成大量对象的表示。
享元模式的原理
享元模式的核心在于分离对象的状态:
-
内在状态(Intrinsic State):
- 对象中可以共享、不变的部分。
- 例如字母的实际内容(‘A’、'B’等)就是内在状态。
-
外在状态(Extrinsic State):
- 对象中不可以共享、经常变化的部分。
- 例如字母的字体、颜色、大小等就是外在状态,由客户端传递。
通过将状态分离,享元模式可以将内在状态共享,避免重复创建对象,进而节省内存。
享元模式的组成
享元模式通常包含以下几个角色:
- Flyweight(享元接口):定义享元对象的公共接口。
- ConcreteFlyweight(具体享元类):实现享元接口,表示可以共享的具体对象。
- UnsharedConcreteFlyweight(非共享享元类,可选):表示不需要共享的享元类。
- FlyweightFactory(享元工厂):管理享元对象的创建和共享,确保相同的享元对象只会被创建一次。
- Client(客户端):使用享元对象,将外在状态传递给享元对象。
案例:文字处理系统中的字符对象
场景描述
我们需要实现一个简单的文字处理系统,其中:
- 每个字符用一个对象表示。
- 字符内容(‘A’、'B’等)是固定的,可以共享(内在状态)。
- 字符的字体、大小、颜色等信息是外在状态,由客户端动态传递。
实现目标
- 利用享元模式共享相同的字符对象,避免重复创建。
- 通过外在状态动态调整字符的显示样式。
代码实现
以下是完整的代码实现,输出信息为中文,并配有详细的注释。
#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>// 抽象享元类:Flyweight
class Flyweight {
public:// 展示字符信息,结合外在状态进行操作virtual void display(const std::string& 字体, int 大小, const std::string& 颜色) const = 0;virtual ~Flyweight() = default;
};// 具体享元类:ConcreteFlyweight
class ConcreteFlyweight : public Flyweight {
private:char 内在状态; // 内在状态,表示字符内容public:explicit ConcreteFlyweight(char c) : 内在状态(c) {}// 实现展示方法void display(const std::string& 字体, int 大小, const std::string& 颜色) const override {std::cout << "字符: " << 内在状态 << ", 字体: " << 字体 << ", 大小: " << 大小 << ", 颜色: " << 颜色 << std::endl;}
};// 享元工厂类:FlyweightFactory
class FlyweightFactory {
private:std::unordered_map<char, std::shared_ptr<Flyweight>> flyweights; // 存储享元对象public:// 获取享元对象std::shared_ptr<Flyweight> getFlyweight(char c) {// 如果享元对象不存在,则创建一个新的对象if (flyweights.find(c) == flyweights.end()) {flyweights[c] = std::make_shared<ConcreteFlyweight>(c);std::cout << "创建新的享元对象: " << c << std::endl;}return flyweights[c];}// 获取享元对象的总数size_t getFlyweightCount() const {return flyweights.size();}
};// 客户端代码
int main() {FlyweightFactory 工厂; // 创建享元工厂// 获取并使用享元对象auto 字符A = 工厂.getFlyweight('A');auto 字符B = 工厂.getFlyweight('B');auto 字符C = 工厂.getFlyweight('C');// 重复获取相同的享元对象auto 字符A2 = 工厂.getFlyweight('A');// 使用享元对象,传递外在状态字符A->display("微软雅黑", 12, "红色");字符B->display("宋体", 14, "蓝色");字符C->display("楷体", 10, "绿色");字符A2->display("黑体", 16, "黑色");// 输出享元对象的总数std::cout << "享元对象的总数: " << 工厂.getFlyweightCount() << " 个" << std::endl;return 0;
}
运行结果
运行上述代码后,输出结果如下:
创建新的享元对象: A
创建新的享元对象: B
创建新的享元对象: C
字符: A, 字体: 微软雅黑, 大小: 12, 颜色: 红色
字符: B, 字体: 宋体, 大小: 14, 颜色: 蓝色
字符: C, 字体: 楷体, 大小: 10, 颜色: 绿色
字符: A, 字体: 黑体, 大小: 16, 颜色: 黑色
享元对象的总数: 3 个
代码讲解
1. 抽象享元类
class Flyweight {
public:virtual void display(const std::string& 字体, int 大小, const std::string& 颜色) const = 0;virtual ~Flyweight() = default;
};
- 定义了一个抽象接口
display
,表示字符的展示方法。 - 外在状态(字体、大小、颜色)由客户端传递。
2. 具体享元类
class ConcreteFlyweight : public Flyweight {
private:char 内在状态; // 内在状态public:explicit ConcreteFlyweight(char c) : 内在状态(c) {}void display(const std::string& 字体, int 大小, const std::string& 颜色) const override {std::cout << "字符: " << 内在状态 << ", 字体: " << 字体 << ", 大小: " << 大小 << ", 颜色: " << 颜色 << std::endl;}
};
内在状态
(字符内容)在对象创建时确定,表示可共享的部分。display
方法结合外在状态动态显示字符信息。
3. 享元工厂类
class FlyweightFactory {
private:std::unordered_map<char, std::shared_ptr<Flyweight>> flyweights;public:std::shared_ptr<Flyweight> getFlyweight(char c) {if (flyweights.find(c) == flyweights.end()) {flyweights[c] = std::make_shared<ConcreteFlyweight>(c);std::cout << "创建新的享元对象: " << c << std::endl;}return flyweights[c];}size_t getFlyweightCount() const {return flyweights.size();}
};
- 工厂管理共享对象的创建与缓存。
- 如果享元对象不存在,则创建;如果已经存在,则直接返回。
4. 客户端代码
字符A->display("微软雅黑", 12, "红色");
字符B->display("宋体", 14, "蓝色");
字符C->display("楷体", 10, "绿色");
字符A2->display("黑体", 16, "黑色");
- 客户端从工厂获取享元对象,并传递外在状态来控制字符的显示。
- 字符
A
和A2
实际上是同一个对象,只是使用了不同的外在状态。
享元模式的优缺点
优点
- 内存节省:通过共享对象,避免了重复创建相似对象,大幅降低内存开销。
- 性能提升:减少了对象的创建和销毁,提升系统性能。
- 统一管理:通过工厂集中管理共享对象,便于维护。
缺点
- 复杂性增加:需要区分内在状态和外在状态,设计复杂度增加。
- 适用场景有限:只有当对象中有可共享部分时,享元模式才适用。
适用场景
- 文字处理系统:字母、数字等字符对象共享。
- 图形系统:共享重复出现的形状或纹理。
- 游戏开发:共享重复出现的敌人、道具等。
总结
享元模式通过共享内在状态,有效减少了内存开销和对象创建的成本,是一种针对性能优化的设计模式。在需要处理大量重复对象的场景中,享元模式是一个非常实用的解决方案。
本文由mdnice多平台发布
相关文章:

C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
什么是享元模式? 享元模式是一个非常实用的结构型设计模式,它的主要目的是节省内存,尤其在需要创建大量相似对象时。 通俗解释: 想象我们在写一本书,每个字母都需要表示出来。如果每个字母都单独用对象表示ÿ…...

android EditText密码自动填充适配
android上的密码(其实不仅仅是密码,可以是用户名也可以是邮箱)自动填充,是需要考虑适配的。 官方文档:https://developer.android.com/identity/autofill/autofill-optimize?hlzh-cn 什么是自动填充 手机厂商一般会…...

LeetCode 刷题笔记
LeetCode 刷题笔记 1. 20241218 (1)2447 std::gcd是C17引入的一个函数,用于计算两个整数的最大公因数。位于<numeric>头文件中。 #include <iostream> #include <numeric> // std::gcdint main() {int a 36;int b 60…...

【Java基础面试题034】Java泛型擦除是什么?
回答重点 泛型擦除指的是Java编译器在编译时将所有泛型信息删除的过程,以确保与Java1.4及之前的版本保持兼容 泛型参数在运行时会被替换为其上界(通常是Object),这样一来在运行时无法获取泛型的实际类型。 作用:泛型…...

使用ssh命令远程登录服务器的两种便捷方式:简化ssh命令、创建bat文件
1. 简化ssh命令 使用记事本打开该路径C:\Users\<你的用户名>\.ssh\下的config文件,粘贴以下代码: Host myserverHostName 192.168.1.1(这里换成你的ip地址)User your_username(这里换成你的用户名)Port 22保存文件后现在在cmd中直接输入ssh myserv…...

access数据库代做/mysql代做/Sql server数据库代做辅导设计服务
针对Access数据库、MySQL以及SQL Server数据库的代做和辅导设计服务,以下是一些关键信息和建议: 一、服务概述 这些服务通常包括数据库的设计、创建、优化、维护以及相关的编程和查询编写等。无论是Access这样的桌面关系数据库管理系统(RDB…...

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题
第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书-A 目录 一、竞赛阶段 二、竞赛任务书内容 (一)拓扑图 (二)模块A 基础设施设置与安全加固(200分) (三)B模块安全事件响应/网络安全数据取证/…...

Android学习(五)-Kotlin编程语言-面向对象中的 继承-构造函数-接口三模块学习
首先,我们需要定义一个 Person 类: open class Person {var name ""var age 0fun eat() {println("$name is eating.")} } 注意,Person 类前面加上了 open 关键字,表示这个类可以被继承。在 Kotlin 中&am…...

滑动窗口 + 算法复习
维护一个满足条件的窗口大小,然后进行双指针移动 1.最长子串 题目链接:1.最长子串 - 蓝桥云课 #include<bits/stdc.h> #define int long long using namespace std; string s; int k; signed main() {int max_len0,left0;cin>>s>>k;…...

贪心算法 greedy
文章目录 参考贪心算法[Leetcode455 分发饼干](https://leetcode.cn/problems/assign-cookies/description/)分析题解 [Leetcode135 分发糖果](https://leetcode.cn/problems/assign-cookies/description/)分析题解 leetcode435无重叠区间分析题解 参考 https://github.com/ch…...

基于python的家教预约网站-家教信息平台系统
标题:基于 Python 的家教预约网站-家教信息平台系统 内容:1.摘要 本文介绍了一个基于 Python 的家教预约网站-家教信息平台系统。该系统旨在为学生和家长提供一个方便、高效的家教预约平台,同时也为家教老师提供一个展示自己教学能力和经验的机会。本文详细介绍了系…...

基于深度学习多图像融合的屏幕缺陷检测方案
公司项目,已申请专利。 深度学习作为新兴技术在图像领域蓬勃发展,因其自主学习图像数据特征的性能避免了人工设计算法的繁琐,精准的检测性能、高效的检测效率以及对各种不同类型的图像任务都有比较好的泛化性能,使得深度学习技术在…...

MySQL基础笔记(三)
在此特别感谢尚硅谷-康师傅的MySQL精品教程 获取更好的阅读体验请前往我的博客主站! 如果本文对你的学习有帮助,请多多点赞、评论、收藏,你们的反馈是我更新最大的动力! 创建和管理表 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数…...

【JetPack】WorkManager笔记
WorkManager简介: WorkManager 是 Android Jetpack 库中的一个重要组件。它用于处理那些需要在后台可靠执行的任务,这些任务可以是一次性的,也可以是周期性的,甚至是需要满足特定条件才执行的任务。例如,它可以用于在后…...

docker 安装 ftp
前言 经多次测试 不知道为什么 必须添加被动模式跟端口才可以 连接成功,有知道为什么可以评论下 下载镜像 docker pull fauria/vsftpd启动ftp 服务 参考链接 docker run -d -v /etc/localtime:/etc/localtime:ro -v /home/dr/data/ftp:/home/vsftpd \ -e "…...

5.C语言内存分区-堆-栈
目录 内存分区 运行之前 代码区 全局初始化数据区 、静态数据区 (data) 未初始化数据区(bss(Block Started by Symbol)区) 总结 运行之后 代码区 (text segment) 未初始化数据区(bss) 全局初始化数据区,静态…...

传统CV算法——基于opencv的答题卡识别判卷系统
基于OpenCV的答题卡识别系统,其主要功能是自动读取并评分答题卡上的选择题答案。系统通过图像处理和计算机视觉技术,自动化地完成了从读取图像到输出成绩的整个流程。下面是该系统的主要步骤和实现细节的概述: 1. 导入必要的库 系统首先导入…...

国产 HighGo 数据库企业版安装与配置指南
国产 HighGo 数据库企业版安装与配置指南 1. 下载安装包 访问 HighGo 官方网站(https://www.highgo.com/),选择并下载企业版安装包。 2. 上传安装包到服务器 将下载的安装包上传至服务器,并执行以下命令: [rootmas…...

「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息,页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。 关键词 UI互动应用天气预报数据绑定动态展示状态管理 一、功能说明 自定义…...

Springboot @Transactional使用时需注意的几个问题
一、事务的隔离级别 在Springboot应用中,如果我们想实现方法一旦执行有异常产生,就触发事务回滚,可以在方法上面添加Transactional注解。如果应用采用mysql数据库,虽然mysql本身也有事务隔离机制,但在Sping数据库的应…...

数字经济下的 AR 眼镜
目录 1. 📂 AR 眼镜发展历史 1.1 AR 眼镜相关概念 1.2 市面主流 XR 眼镜 1.3 AR 眼镜大事记 1.4 国内外 XR 眼镜 1.5 国内 AR 眼镜四小龙 2. 🔱 关键技术 2.1 AR 眼镜近眼显示原理 2.2 AR 眼镜关键技术 2.3 AR 眼镜技术难点 3. Ὂ…...

力扣150题
88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 **注意:**…...

剑指offer搜索二维矩阵
题目连接 https://leetcode.cn/problems/search-a-2d-matrix-ii/’ 代码 自己想出来的 解法一 初始化两个指针,i0,j列数-1 若此时matrix[i][j]target 则返回true 若此时matrix[i][j]>target,表明在第j列中不可能存在target,因为列是升序的 若此时ma…...

如何设置浏览器不缓存网页
设置浏览器不缓存网页可以通过多种方法实现,以下是一些常见的策略: HTTP响应头控制: Cache-Control:这是最常用的HTTP头之一,用于控制响应的缓存行为。例如: Cache-Control: no-cache, no-store, must-r…...

Iris简单实现Go web服务器
package mainimport ("github.com/kataras/iris" )func main() {app : iris.New() // 实例一个iris对象//配置路由app.Get("/", func(ctx iris.Context) {ctx.WriteString("Hello Iris")})app.Get("/aa", func(ctx iris.Context) {ct…...

后端项目java中字符串、集合、日期时间常用方法
我这里只介绍了项目中最常用的哈,比如像集合有很多,但我们最常用的就是ArrayList。 然后我这里会以javascript中的字符串、数组的方法为基准来实现,有些方法js和java会有些区别也会介绍 字符串 每次修改 String 对象都会创建一个新的对象,而 StringBuffer 可以在同一个对象…...

【Spring事务】深入浅出Spring事务从原理到源码
什么是事务 保证业务操作完整性的一种数据库机制 (driver 驱动)事务特定 ACID A 原子性 (多次操作 要不一起成功 要不一起失败 (部分失败 savepoint)) C 一致性 (事务开始时数据状态,…...

vue.js滑动到顶便锁定位置
<template><div><div class"nav"></div><div class"searchBar" id"searchBar"><ul :class"searchBarFixed true ? isFixed :"> <li>区域<i class"iconfont icon-jiantouxia"…...

EdgeX Core Service 核心服务之 Core Command 命令
EdgeX Core Service 核心服务之 Core Command 命令 一、概述 Core-command(通常称为命令和控制微服务)可以代表以下角色向设备和传感器发出命令或动作: EdgeX Foundry中的其他微服务(例如,本地边缘分析或规则引擎微服务)EdgeX Foundry与同一系统上可能存在的其他应用程序…...

掌握常用HTML标签:创建个人简介网页
任务目标 理解HTML文档的基本结构,掌握常见的HTML标签及其用途,创建一个简单的个人简介网页。 学习内容脑图 #mermaid-svg-5GTdqH41gawr4v0h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...