C++设计模式——Flyweight享元模式

一,享元模式简介

享元模式是一种结构型设计模式,它将每个对象中各自保存一份数据的方式改为多个对象共享同一份数据,该模式可以有效减少应用程序的内存占用。
享元模式的核心思想是共享和复用,通过设置共享资源来避免创建过多的实例。
当应用程序的内部包含大量的对象,且对象之间包含相似的数据或状态时,可以使用享元模式来共享这些数据或状态。
享元模式的内部涉及到工厂模式的使用,因为它需要创建一个享元工厂来管理共享资源池。这个共享资源池,又称为享元池(Flyweight Pool),这里面包含多个访问共享数据的享元对象。当客户端需要使用一个享元对象时,享元工厂会从池中获取一个已有的享元对象,如果对象不存在,则创建一个新的享元对象。

二,享元模式的结构

1.内部状态(Intrinsic State):对象之间容易重复的、可以共享的、且变动很少的成员变量,该变量在享元模式中被共享。
2.外部状态(Extrinsic State):对象之间各不相同的、不能共享的、且随着不同场景而变化的成员变量,该变量被调用的客户端所设置和更改。
3.享元工厂类(Flyweight Factory):替外部客户端管理共享资源的类。
4.抽象享元类(Flyweight):享元模式的核心,由享元工厂进行创建和管理,里面包含了内部状态,但不包含外部状态。
5.共享的具体享元类(Concrete Flyweight):实现了Flyweight声明的接口并访问和存储了内部状态。
对应UML类图:

代码实现:

#include <iostream>
#include <memory>
#include<unordered_map>

using namespace std;

class Flyweight {
protected:
    int id; //内部状态

public:
    Flyweight(int id) : id(id) {}


    virtual void operation() const = 0;
};

class ConcreteFlyweightA : public Flyweight {
public:
    ConcreteFlyweightA() : Flyweight(1) {}
    void operation() const override {
        cout << "Concrete Flyweight A, id: " << id << endl;
    }
};

class ConcreteFlyweightB : public Flyweight {
public:
    ConcreteFlyweightB() : Flyweight(2) {}
    void operation() const override {
        cout << "Concrete Flyweight B, id: " << id << endl;
    }
};

// 定义享元工厂
class FlyweightFactory {
private:
    std::unordered_map<int, shared_ptr<Flyweight>> flyweights;

public:
    FlyweightFactory() {}

    //返回享元对象
    std::shared_ptr<Flyweight> getConcreteFlyweight(int id) {
        if (flyweights.find(id) == flyweights.end()) {
            if (id % 2 == 0) {
                flyweights[id] = make_shared<ConcreteFlyweightA>();
            }
            else {
                flyweights[id] = make_shared<ConcreteFlyweightB>();
            }
        }
        return flyweights[id];
    }
};

int main() {
    FlyweightFactory factory;
    shared_ptr<Flyweight> f1 = factory.getConcreteFlyweight(1);
    shared_ptr<Flyweight> f2 = factory.getConcreteFlyweight(2);
    shared_ptr<Flyweight> f3 = factory.getConcreteFlyweight(3);

    f1->operation();
    f2->operation();
    f3->operation();

    return 0;
}

运行结果:

Concrete Flyweight B, id: 2
Concrete Flyweight A, id: 1
Concrete Flyweight B, id: 2

三,享元模式的工作步骤

1.拆分类的成员变量,将成员变量拆分成以下两种:不变的、可能在对象之间重复使用的。变化的、随着应用场景而改动的。
2.将不变的,可重复的成员变量的属性设置为不可修改,并在构造函数中赋初始值。
3.创建享元类,并将共享的成员变量集成到享元类。
4.创建享元工厂类来管理共享的资源池,客户端与享元对象的交互借助享元工厂来实现。
5.优化共享资源池的代码实现,这可能涉及到事件驱动、回调函数或者策略模式的应用。

四,享元模式的应用场景

图形或图像处理:在大型游戏或图形编辑器开发中,同一个形状(如矩形)或颜色等状态会重复出现很多次,基于享元模式可以降低内存开销。
数据库处理优化:数据库被频繁地连接和请求时,享元模式可以管理这些连接并复用它们,提高处理的性能。
UI组件开发:在用户界面中,当创建多个界面窗口时,像按钮、图标等小部件会在创建界面窗口时有大量重复,使用享元模式可以减少界面之间重复组件的数量,提高性能。

五,享元模式的优缺点

享元模式的优点:
1.增加了系统资源的可重用性,节省了系统资源。
2.基于共享的结构,降低了内存消耗。
3.系统可扩展性强,新增对象时可直接复用共享资源。
4.降低了对象内部的结构复杂性。
享元模式的缺点:
1.使代码结构更加复杂。
2.当需要被共享的资源量级很小时,该模式的性能提升并不显著。
3.将共享变量放在构造函数中进行赋值,额外增加了初始化的时间。
4.引入了"共享"这种结构,会导致潜在的线程安全问题。
5.编写代码需要考虑保证状态的同步和一致性问题,否则会导致bug的产生。

六,代码实战

Demo:模拟字符编辑器
#include <iostream>
#include <map>

using namespace std;

//Flyweight
class Character {
public:
    Character(char symbol) : symbol_(symbol) {}
    Character() = default;

    void print() const {
        cout << "Character: " << symbol_ << endl;
    }

private:
    char symbol_;
};

//Flyweight factory
class CharacterFactory {
public:
    static const Character& getCharacter(char symbol) {
        if (characters_.find(symbol) == characters_.end()) {
            characters_[symbol] = Character(symbol);
            cout << "Create new char." << endl;
        }
        return characters_[symbol];
    }

private:
    static map<char, Character> characters_;
};

map<char, Character> CharacterFactory::characters_;

int main() {
    const Character& A = CharacterFactory::getCharacter('A');
    const Character& B = CharacterFactory::getCharacter('B');

    //Reusing 'A'
    const Character& A2 = CharacterFactory::getCharacter('A');

    A.print();
    B.print();
    A2.print();

    return 0;
}

运行结果:

Create new char.
Create new char.
Character: A
Character: B
Character: A

七,参考阅读

https://softwarepatterns.com/cpp/flyweight-software-pattern-cpp-example
https://www.pentalog.com/blog/design-patterns/flyweight-design-patterns/
https://design-patterns.readthedocs.io/zh-cn/latest/structural_patterns/flyweight.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/736634.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql高级语句2存储过程

CREATE VIEW 视图&#xff0c;可以被当作是虚拟表或存储查询。 视图跟表格的不同是&#xff0c;表格中有实际储存数据记录&#xff0c;而视图是建立在表格之上的一个架构&#xff0c;它本身并不实际储存数据记录。 临时表在用户退出或同数据库的连接断开后就自动消失了&…

C++20中的Feature Test Mocros

C20定义了一组预处理器宏&#xff0c;用于测试各种语言和库的feature。 Feature Test Mocros(特性测试宏)是C20中引入的一种强大机制&#xff0c;用于应对兼容性问题。Feature Test Mocros作为预处理器指令(preprocessor directives)出现&#xff0c;它使你能够在编译过程中仔细…

python输入、输出和变量

一、变量 变量是存储数据的容器。在 Python 中&#xff0c;变量在使用前不需要声明数据类型&#xff0c;Python 会根据赋值自动推断变量类型。 定义变量&#xff1a; 二、输入&#xff08;Input&#xff09; input() 函数用于获取用户输入。默认情况下&#xff0c;input() 会…

系统因果性

1、因果系统的定义   若系统当前输出仅由系统当前或过去的输入决定&#xff0c;则称该系统为因果系统。由于因果系统是物理课实现的&#xff0c;且具有稳定性和可预测性&#xff0c;所以得到广泛应用。设系统输入、输出分别为 x ( t ) x(t) x(t)&#xff0c; y ( t ) y(t) y(t…

描述React Hooks中的useMemo和useCallback的区别和用途。

React Hooks API中的useMemo和useCallback都是用于优化性能的钩子&#xff0c;但它们的用途和工作方式略有不同&#xff1a; 推荐大家看看我过往的文章 useMemo useMemo是一个性能优化钩子&#xff0c;它返回一个记忆化的值。useMemo可以避免在组件渲染时进行昂贵的计算或操作…

【html】用html+css模拟Windows右击菜单

效果图&#xff1a; 在这个示例中&#xff0c;我为每个.second-list添加了一个.sub-menu的<div>&#xff0c;它包含了子菜单项。当鼠标悬停在.second-list上时&#xff0c;.sub-menu会显示出来。你可以根据需要调整这个示例以适应你的具体需求。 记住&#xff0c;这只是…

SpringMVC系列六: 视图和视图解析器

视图和视图解析器 &#x1f49e;基本介绍&#x1f49e; 自定义视图为什么需要自定义视图自定义试图实例-代码实现自定义视图工作流程小结Debug源码默认视图解析器执行流程多个视图解析器执行流程 &#x1f49e;目标方法直接指定转发或重定向使用实例指定请求转发流程-Debug源码…

谁说串口通信波特率越高越好?

在电子世界里&#xff0c;串口通信就像是电子设备之间的“悄悄话”&#xff0c;它们通过串行数据传输来交换信息。但你知道吗&#xff1f;串口通信的波特率并不是越高越好&#xff0c;这事儿得好好聊聊。 1.什么是串口通信&#xff1f; 串口通信&#xff0c;就像它的名字一样&a…

cherry 笔记三(启动)

cherry启动很简单 app创建完 直接startup()就好了 func main() {app : cherry.Configure("./examples/config/profile-chat.json","chat-master",false,cherry.Cluster,)app.SetSerializer(cserializer.NewJSON())app.Startup() } Configure()--->N…

【Python机器学习】NMF——将NMF应用于人脸图像

将NMF应用于之前用过的Wild数据集中的Labeled Faces。NMF的主要参数是我们想要提取的分量个数。通常来说&#xff0c;这个数字要小于输入特征的个数&#xff08;否则的话&#xff0c;将每个像素作为单独的分量就可以对数据进行解释&#xff09;。 首先&#xff0c;观察分类个数…

汽车销售系统

摘 要 在现代社会&#xff0c;电脑是企业运作和管理必不可少的工具。我们过去用手记下卖出的商品的年代已一去不复返了。在我国&#xff0c;汽车销售行业的竞争日趋激烈的情况下&#xff0c;如何提高企业的管理水平&#xff0c;提高企业的工作效率&#xff0c;提高企业的服务质…

ARC学习(3)基本编程模型认识(三)

笔者来介绍arc的编程模型的中断流程和异常流程 1、中断介绍 主要介绍一下中断进入的流程&#xff0c;包括需要配置的寄存器等信息。 中断号&#xff1a;16-255&#xff0c;总共240个中断。触发类型&#xff1a;脉冲或者电平触发中断优先级&#xff1a;16个&#xff0c;0最大&…

DAC测试实验——FPGA学习笔记7

一、DAC简介 DAC全称Digital to Analog Converter&#xff0c;即数模转换器。它用于将主控芯片产生的数字值(0和1)转换为模拟值(电压值)。 1、DAC参数指标 2、DAC类型 常用的DAC可大致分为权电阻网络DAC、T型电阻网络DAC、倒T型电阻网络DAC以及权电流型DAC。 3、AD9708/3PD9…

RSA 加密算法的基础数论、基本原理与 Python 实现 (包含扩展欧几里得算法)

Title: RSA 加密算法的基础数论、基本原理与 Python 实现&#xff08;包含扩展欧几里得算法&#xff09; 文章目录 前言I. 数学原理1. 整数环2. 单位元3. 欧拉定理 II. 算法原理1. 扩展欧几里得算法2. RSA 非对称加密算法 III. 算法实现1. 源代码2. 测试结果 总结参考文献 前言…

spdlog生产者消费者模式

spdlog生产者消费者模式 spdlog提供了异步模式&#xff0c;显示的创建async_logger, 配合环形队列实现的消息队列和线程池实现了异步模式。异步logger提交日志信息和自身指针&#xff0c; 任务线程从消息队列中取出消息后执行对应的sink和flush动作。 1. 环形队列 1.1 环形队…

智慧校园综合门户有哪些特点?

智慧校园的门户系统&#xff0c;作为整个智慧校园架构的门户窗口&#xff0c;扮演着至关重要的角色。它如同一座桥梁&#xff0c;将校园内的各种信息资源、应用服务以及管理功能紧密相连&#xff0c;为师生、家长及管理人员提供了一个集中访问的便捷通道。智慧校园门户的设计理…

RPC通信原理以及项目的技术选型

目录 1.引言 2、RPC通信原理 3.图示解析 4.再举个例子 1.引言 根据上一篇博客《单机&#xff0c;集群和分布式》的举的例子。 我们最终合理地通过对大型软件的合理划分&#xff0c;划分成不同模块&#xff0c;按需求&#xff08;硬件需求&#xff0c;高并发需求&#xff09…

Hadoop+Spark大数据技术(微课版)总复习

期末试卷组成 一、选择题(每小题2分&#xff0c;共20分) 二、判断题(每小题2分&#xff0c;共20分) 三、简答题(每小题5分&#xff0c;共20分) 四、程序分析题 (第1-5小题各6分&#xff0c;第6题10分&#xff0c;共40分) 图1 Hadoop开发环境 图2 HDFS 图3 MapReduce 图4 H…

IDEA插件推荐-CodeGeex

功能&#xff1a;这个插件可以实现快速翻译代码&#xff0c;json文件格式转换&#xff0c;代码语言类型转换。 安装方式&#xff1a;File->Settings->Plugins->MarketPlace->搜索“CodeGeex”即可 &#xff08;CodeGeex功能展示&#xff09; &#xff08;CodeGeex…

【科普】半导体制造过程的步骤、技术、流程

在这篇文章中&#xff0c;我们将学习基本的半导体制造过程。为了将晶圆转化为半导体芯片&#xff0c;它需要经历一系列复杂的制造过程&#xff0c;包括氧化、光刻、刻蚀、沉积、离子注入、金属布线、电气检测和封装等。 基本的半导体制造过程 1.晶圆&#xff08;Wafer&#xf…