结构型设计模式之亨元模式【设计模式系列】

news2024/11/22 15:07:26

系列文章目录

C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
设计模式系列

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

结构型设计模式之亨元模式

  • 系列文章目录
  • 一、亨元模式介绍
  • 二、亨元模式优缺点
    • 2.1 优点
    • 2.2 缺点
  • 三、亨元模式使用场景
  • 四、亨元模式实现
  • 五、总结

一、亨元模式介绍

亨元模式?不知道为啥起这么奇怪的名字!!!

简单的理解: 一个类的成员非常多,创建此对象很消耗资源,在实际场景中又需要反复创建和销毁该对象。所消耗的内存,就更加庞大。

如果此时设计一个对象池,里面缓存一定的对象,软件在用时申请,不用时回收。就能实现对象的重复利用,而多次创建和销毁对象。

什么是享元模式?

说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。

比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。

举个最简单的例子,网络联机下棋的时候,一台服务器连接了多个客户端(玩家),如果我们每个棋子都要创建对象,那一盘棋可能就有上百个对象产生,玩家多点的话,因为内存空间有限,一台服务器就难以支持了,所以这里要使用享元模式,将棋子对象减少到几个实例。

⚠️ 意图:
运用共享技术有效地支持大量细粒度的对象。

⚠️ 主要解决:
在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。。

⚠️ 何时使用:
1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。

⚠️ 如何解决:
用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。

在这里插入图片描述

图1_1 亨元模式类图

因为要求细粒度对象,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。

内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

二、亨元模式优缺点

2.1 优点

大大减少了对象的创建,降低了程序内存的占用,提高效率

  • 享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

2.2 缺点

提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变

  • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度的减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

也就是说,享元模式Flyweight执行时所需的状态是有内部的也可能有外部的,内部状态存储于ConcreteFlyweight对象之中,而外部对象则应该考虑由客户端对象存储或计算,当调用Flyweight对象的操作时,将该状态传递给它。

三、亨元模式使用场景

  • 系统中存在大量相似对象
  • 需要缓冲池的场景

注意事项:
注意划分内部状态和外部状态,否则可能会引起线程安全问题;
这些类必须有一个工厂类加以控制。

四、亨元模式实现

Flyweight抽象类
所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

#ifndef FLYWEIGHT_FLYWEIGHT_H
#define FLYWEIGHT_FLYWEIGHT_H

#include <iostream>
#include <string>
#include <utility>

using namespace std;
class Flyweight {
public:
    explicit Flyweight(string extrinsic) {
        extrinsic_ = std::move(extrinsic);
    }

    virtual ~Flyweight() = default;
    //定义业务操作
    virtual void operate(int extrinsic) = 0;

    string getIntrinsic() const {
        return intrinsic_;
    }

    void setIntrinsic(string intrinsic) {
        intrinsic_ = std::move(intrinsic);
    }

public:
    string intrinsic_; //内部状态
protected:
    //要求享元角色必须接受外部状态
    string extrinsic_;//外部状态

};


#endif //FLYWEIGHT_FLYWEIGHT_H

ConcreteFlyweight类
继承Flyweight超类或实现Flyweight接口,并为其内部状态增加存储空间。

#ifndef FLYWEIGHT_CONCRETEFLYWEIGHT_H
#define FLYWEIGHT_CONCRETEFLYWEIGHT_H

#include "Flyweight.h"

class ConcreteFlyweight : public Flyweight{
    //接受外部状态
public:
    explicit ConcreteFlyweight(string extrinsic) : Flyweight(std::move(extrinsic)) {}
    ~ ConcreteFlyweight() = default;
    //根据外部状态进行逻辑处理
    void operate(int extrinsic) override {
        cout << "具体Flyweight:" << extrinsic << endl;
    }
};


#endif //FLYWEIGHT_CONCRETEFLYWEIGHT_H

UnsharedConcreteFlyweight类
指那些不需要共享的Flyweight子类。

#ifndef FLYWEIGHT_UNSHAREDCONCRETEFLYWEIGHT_H
#define FLYWEIGHT_UNSHAREDCONCRETEFLYWEIGHT_H

#include "Flyweight.h"

class UnsharedConcreteFlyweight : public Flyweight{
public:
    explicit UnsharedConcreteFlyweight(string extrinsic) : Flyweight(std::move(extrinsic)) {}
    ~UnsharedConcreteFlyweight() = default;
    //根据外部状态进行逻辑处理
    void operate(int extrinsic) override {
        cout << "不共享的具体Flyweight:" << extrinsic << endl;
    }
};


#endif //FLYWEIGHT_UNSHAREDCONCRETEFLYWEIGHT_H

FlyweightFactory类
一个享元工厂,用来创建并管理Flyweight对象,主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或创建一个实例。

#include "Flyweight.h"
#include "ConcreteFlyweight.h"
#include <map>
#include <memory>

using namespace std;

class FlyweightFactory {
public:
    FlyweightFactory() = default;
    ~FlyweightFactory() = default;

    //享元工厂
public:
     shared_ptr<Flyweight> GetFlyweight(string extrinsic) {
        cout << extrinsic << endl;
        shared_ptr<Flyweight> flyweightPtr = nullptr;
        auto a = pool_.find(extrinsic);
        if(pool_.find(extrinsic) != pool_.end()) {    //池中有该对象
            flyweightPtr = pool_.at(extrinsic);
            cout << "已有 " << extrinsic << " 直接从池中取---->" << endl;
        } else {
            //根据外部状态创建享元对象
            flyweightPtr = make_shared<ConcreteFlyweight>(extrinsic);
            //放入池中
            pool_.emplace(extrinsic, flyweightPtr);
           cout << "创建 " << extrinsic  <<  " 并从池中取出---->" << endl;
        }

        return flyweightPtr;
    }

    //定义一个池容器
private:
     map<string, shared_ptr<Flyweight>> pool_;
};


#endif //FLYWEIGHT_FLYWEIGHTFACTORY_H

Client客户端

#include <iostream>
#include "Flyweight.h"
#include "FlyweightFactory.h"
#include "UnsharedConcreteFlyweight.h"

using namespace std;
int main() {
    int extrinsic = 22;

    FlyweightFactory factory;

    shared_ptr<Flyweight> flyweightX = factory.GetFlyweight("X");
    flyweightX->operate(++extrinsic);

    shared_ptr<Flyweight> flyweightY = factory.GetFlyweight("Y");
    flyweightY->operate(++extrinsic);

    shared_ptr<Flyweight> flyweightZ = factory.GetFlyweight("Z");
    flyweightZ->operate(++extrinsic);

    shared_ptr<Flyweight> flyweightReX = factory.GetFlyweight("X");
    flyweightReX->operate(++extrinsic);

    shared_ptr<UnsharedConcreteFlyweight> unsharedFlyweight = make_shared<UnsharedConcreteFlyweight>("X") ;
    unsharedFlyweight->operate(++extrinsic);

    return 0;
}

运行结果如下:
在这里插入图片描述

图1_2 运行结果

从这个结果我们可以看出来,第一次创建X、Y、Z时,都是先创建再从池中取出,而第二次创建X时,因为池中已经存在了,所以直接从池中取出,这就是享元模式。

五、总结

与其他相关模式:

客户端要引用享元对象,是通过工厂对象创建或者获得的,客户端每次引用一个享元对象,都是可以通过同一个工厂对象来引用所需要的享元对象。因此,可以将享元工厂设计成单例模式,这样就可以保证客户端只引用一个工厂实例。因为所有的享元对象都是由一个工厂对象统一管理的,所以在客户端没有必要引用多个工厂对象。不管是单纯享元模式还是复合享元模式中的享元工厂角色,都可以设计成为单例模式,对于结果是不会有任何影响的。

Composite模式:Flyweight模式通常和Composite 模式结合起来,用共享叶结点的有向无环图实现一个逻辑上的层次结构。复合享元模式实际上是单纯享元模式与合成模式的组合。单纯享元对象可以作为树叶对象来讲,是可以共享的,而复合享元对象可以作为树枝对象, 因此在复合享元角色中可以添加聚集管理方法。通常,最好用Flyweight实现State 和Strategy 对象。

小结:

  1. 享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。
  2. 享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享 元池中不存在,则创 建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
  3. 享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。
    (1) 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
    (2) 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候 再传入到享元对 象内部。一个外部状态与另一个外部状态之间是相互独立的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第16章 控制脚本

CtrlC组合键会发送SIGINT信号&#xff0c;停止shell中当前运行的进程。 CtrlZ组合键会生成一个SIGTSTP信号&#xff0c;停止shell中运行的任何进程。停止进程会让程序继续保留在内存中&#xff0c;并能从上次停止的位置继续运行。 方括号中的数字是shell分配的作业号&#xff0…

PHP之Smarty使用以及框架display和assign原理

一、Smarty的下载 进入Smarty官网下载&#xff0c;复制目录libs目录即可http://www.smarty.net/http://www.smarty.net/ 二、使用Smarty&#xff0c;创建目录demo,把libs放进去改名为Smarty 三、引入Smarty配置,创建目录&#xff0c;index.php文件配置 <?php…

VuePress在生产环境跳转子页报错 Failed to execute ‘appendChild‘ on ‘Node‘

记录一个使用VuePress时遇到的问题 使用VuePress做了一个文档网页&#xff0c;在开发环境的时候一切正常&#xff0c;但是发布到生产环境后&#xff0c;直接跳转二级页面会报错Failed to execute appendChild on Node 比如主页是http://sun/docs/.vuepress/dist/index.html#/…

【算法】递增序列

对于一个字母矩阵&#xff0c;我们称矩阵中的一个递增序列是指在矩阵中找到两个字母&#xff0c;它们在同一行&#xff0c;同一列&#xff0c;或者在同一 45 度的斜线上&#xff0c;这两个字母从左向右看、或者从上向下看是递增的 对于下面的 30 行 50列的矩阵&#xff0c;请问…

AtcoderABC234场

A - Weird FunctionA - Weird Function 题目大意 要求计算 f(f(f(t)t)f(f(t))) &#xff0c;其中 t 是一个给定的整数。 函数 f(x) 定义为 f(x) x^2 2x 3。 思路分析 定义实现函数 f(int t)&#xff0c;并嵌套调用。 时间复杂度 O(1) AC代码 #include<bits/stdc.…

STN:Spatial Transformer Networks

1.Abstract 卷积神经网络缺乏对输入数据保持空间不变的能力&#xff0c;导致模型性能下降。作者提出了一种新的可学习模块&#xff0c;STN。这个可微模块可以插入现有的卷积结构中&#xff0c;使神经网络能够根据特征图像本身&#xff0c;主动地对特征图像进行空间变换&#x…

Toyota Programming Contest 2023#4(AtCoder Beginner Contest 311)(A-G)

Contest Duration: 2023-07-22(Sat) 20:00 - 2023-07-22(Sat) 21:40 (local time) (100 minutes) 头文件和宏 #include<iostream> #include<string> #include<vector> using namespace std; #define int long long #define fer(i,a,b) for(int ia;i<b;i…

Python实现HBA混合蝙蝠智能算法优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝙蝠算法是2010年杨教授基于群体智能提出的启发式搜索算法&#xff0c;是一种搜索全局最优解的有效方法…

信息安全与网络空间安全 - 保障您的在线安全

数据参考&#xff1a;CISP官方 目录&#xff1a; 信息与信息安全 信息安全属性 网络安全发展阶段 网络空间安全保障 一、信息与信息安全 1、什么是信息&#xff1f; 定义&#xff1a;信息是通过传递和处理的方式&#xff0c;用于传达知识、事实、数据或观点的内容。形…

k8s安装prometheus

安装 在目标集群上&#xff0c;执行如下命令&#xff1a; kubectl apply -f https://gitee.com/i512team/dhorse/raw/main/conf/kubernetes-prometheus.yml使用 1.在浏览器访问地址&#xff1a;http://master_ip:30000&#xff0c;如下图所示&#xff1a; 2.查看k8s自带的…

模拟Stevens Lewis描述的小型飞机纵向动力学的非线性动态反演控制器研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码实现 &#x1f4a5;1 概述 针对Stevens和Lewis描述的小型飞机纵向动力学的非线性动态&#xff0c;研究非线性动态反演控制器可以是一个有趣的课题。动态反演控制器的目标…

智慧供水调度工控平台-业务数据化,数据模型化

平台概述 智慧供水调度工控平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生态、水…

微信小程序quickstartFunctions中云函数的应用

1、在quickstartFunctions文件中新建文件夹和文件 2、index.js 文件书写 const cloud require(wx-server-sdk);cloud.init({env: cloud.DYNAMIC_CURRENT_ENV }); const db cloud.database();// 链表查询试卷和对应的题库 exports.main async (event, context) > {retu…

现货白银投资如何挂单

如果现货白银投资者不想时时刻刻都在盯盘&#xff0c;盯紧进场的机会&#xff0c;可以采用提前挂单的方式来交易&#xff0c; 这样做的好处很多&#xff0c;尤其是在市场行情波动大的时候&#xff0c;投资者如何手动下单&#xff0c;很难在自己期望的价格成交以&#xff0c;导致…

SAP ERP系统屏幕变式(SHD0)的应用

在SAP/ERP项目实施中屏幕变式技术(SHD0)的应用非常广泛,基本每个项目都会大量的应用,通过屏幕变式技术可以不用任何开发情况下,非常方便将SAP操作界面的某些字段根据业务要求进行锁定、隐藏或设为必须输入。 下面通过一个实际项目案例来了解下屏幕变式的具体应用 业务需…

本地非文字资源无法加载

目录 方法A.静态/动态绑定路径 方法B.require导入&#xff08;运行时加载&#xff09; 方法C.import导入&#xff08;x&#xff09;&#xff08;编译时加载&#xff09; 方法D.ref直接操作元素赋值&#xff08;x&#xff09; 相关知识 import和requir区别 模板路径&#…

NLP(六十二)HuggingFace中的Datasets使用

Datasets库是HuggingFace生态系统中一个重要的数据集库&#xff0c;可用于轻松地访问和共享数据集&#xff0c;这些数据集是关于音频、计算机视觉、以及自然语言处理等领域。Datasets 库可以通过一行来加载一个数据集&#xff0c;并且可以使用 Hugging Face 强大的数据处理方法…

RocketMQ教程-(5)-功能特性-消费者分类

Apache RocketMQ 支持 PushConsumer 、 SimpleConsumer 以及 PullConsumer 这三种类型的消费者&#xff0c;本文分别从使用方式、实现原理、可靠性重试和适用场景等方面为您介绍这三种类型的消费者。 背景信息​ Apache RocketMQ 面向不同的业务场景提供了不同消费者类型&…

巅峰极客2023 hellosql

随便输一个payload&#xff0c;有waf 这题只有两个回显&#xff0c;分别是太酷啦和nonono&#xff0c;不显示报错、登录成功等各种信息&#xff0c;目前只能想到用时间盲注。 抓包fuzz&#xff0c;194都是被过滤的 不止这些&#xff0c;手工测出来if、sleep、benchmark、*、rp…

HOOPS Visualize | HOOPS Exchange | HOOPS Web Platform

内容采集互联网&#xff0c;功能仅供参考&#xff01;&#xff01;&#xff01; HOOPS 3D Application Framework (HOOPS/3dAF)是由Tech Soft America公司开发并由Spatial再次销售的产品&#xff0c;该产品为当今世界上领先的3D应用程序提供了核心的图形架构和图形功能&#x…