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

系列文章目录

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.mzph.cn/news/9266.shtml

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

相关文章

MySQL 主从复制的认识 2023.07.23

一、理解MySQL主从复制原理 1、概念&#xff1a;主从复制是用来建立一个和 主数据库完全一样的数据库环境称为从数据库&#xff1b;主数据库一般是准实时的业务数据库。 2、作用&#xff1a;灾备、数据分布、负载平衡、读写分离、提高并发能力 3、原理图 4、具体步骤 (1) M…

Windows nvm 安装后webstrom vue项目编译报错,无法识别node

1 nvm安装流程 卸载原先nodejs用管理员权限打开exe安装nvmnvm文件夹和nodejs文件夹 都授权Authenticated Users 完全控制nvm list availablenvm install 16.20.1nvm use 16.20.1输入node和npm检查版本命令&#xff0c;正常显示确认系统变量和用户变量都有nvm 和nodejs 2 bug情…

iTOP-RK3568开发板Docker 安装 Ubuntu 18.04

Docker 下载安装 Ubuntu18.04&#xff0c;输入以下命令&#xff1a; sudo apt update docker pull ubuntu:18.04 切换 Shell 到 Ubuntu 18.04&#xff0c;输入以下命令&#xff1a; docker container run -p 8000:3000 -it ubuntu:18.04 /bin/bash -p 参数&#xff1a;容器的…

Ftp和UDP的区别之如何加速文件传输

FTP&#xff08;文件传输协议&#xff09;是一种传输大文件的老方法&#xff0c;它的速度慢&#xff0c;而且容易受到网络环境的影响。在当今这个文件越来越大&#xff0c;项目交付时间越来越紧&#xff0c;工作分布在全球各地的时代&#xff0c;有没有办法让 FTP 加速呢&#…

大数据课程D2——hadoop的概述

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解hadoop的定义和特点&#xff1b; ⚪ 掌握hadoop的基础结构&#xff1b; ⚪ 掌握hadoop的常见命令&#xff1b; ⚪ 了解hadoop的执行流程&#xff1b; 一、简介 1…

adnroid 11. 0 Activity启动流程图解

从Launcher到ActivityTaskManager 从ActivityTaskManagerService 到 ApplicationThread 从ApplicationThread到onCreate

mac 安装 php 与 hyperf 框架依赖的扩展并启动 gptlink 项目

m系列 mac 安装 php 与 hyperf 框架依赖的扩展并启动 gptlink 项目 gptlink 项目是一个前后端一体化的 chatgpt 开源项目 gptlink 项目地址&#xff1a;https://github.com/gptlink/gptlink 安装 php 8.0 版本&#xff1a; brew install php8.0安装完成后提示如下&#xff…

uniapp 语音文本播报功能

最近uniapp项目上遇到一个需求 就是在接口调用成功的时候加上语音播报 &#xff0c; ‘创建成功’ ‘开始成功’ ‘结束成功’ 之类的。 因为是固定的文本 &#xff0c;所以我先利用工具生成了 文本语音mp3文件&#xff0c;放入项目中&#xff0c;直接用就好了。 这里用到的工…

Rust vs Go:常用语法对比(十一)

题目来自 Rust Vs Go: Which Language Is Better For Developing High-Performance Applications?[1] 202. Sum of squares Calculate the sum of squares s of data, an array of floating point values. 计算平方和 package mainimport ( "math")func main() { da…

OpenCV 4.0+Python机器学习与计算机视觉实战

&#x1f482; 个人网站:【办公神器】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言第一部分&…

TypeScript入门学习汇总

1.快速入门 1.1 简介 TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 标准。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大型应用&#xff0c;它可以编译成纯 JavaScript&#xff0c;编译出来的 JavaScript 可以运行在…

HTTP超本文传输协议

HTTP超本文传输协议 HTTP简介HTTP请求与响应HTTP请求请求行请求头空行请求体 HTTP响应响应行响应头空行响应体 HTTP请求方法GET和POST之间的区别HTTP为什么是无状态的cookie原理session 原理cookie 和 session 的区别cookie如何设置cookie被禁止后如何使用session HTTP简介 HT…

Redis配置与优化

目录 一、关系数据库与非关系型数据库 1、关系型数据库 2、非关系型数据库 3、关系型数据库和非关系型数据库区别 1、数据存储方式不同 2、扩展方式不同 3、对事务性的支持不同 二、Redis 1、简介 2、优点 3、缺点 4、使用场景 5、哪些数据适合放入缓存中 6、为什…

vue-cli脚手架创建创建的项目打包后无法正常打开报 Failed to load resource: net::ERR_FILE_NOT_FOUND错误

亲爱的小伙伴们&#xff0c;你们最近是否有遇到用使用最新的脚手架打包项目后index.html文件无法正常打开&#xff0c;然后控制台报错的情况呢&#xff0c;不要担心&#xff0c;这个坑今天被我踩到了并且被我解决了&#xff0c;下边就让我来给大家分享一下经验吧&#xff01; …

D. Maximum Subarray

Problem - 1796D - Codeforces 思路&#xff1a;想了个假dp做法推了半天&#xff0c;果然是dp。考虑用dp[i][j]表示以i结尾的&#xff0c;并且选择j个&#xff0b;x的最长连续子序列&#xff0c;那么如果我不选择第i位&#xff0c;那么会有f[i][j]max(w[i]-x,f[i-1][j]w[i]-x)&…

Kubernetes 的核心概念:Pod、Service 和 Namespace 解析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Linux共享库库+例子

1.什么是共享库&#xff1f;有什么优点&#xff1f;和静态库有什么区别&#xff1f; Linux动态库&#xff08;Dynamic Link Library&#xff0c;缩写为DLL&#xff09;是一种在Linux系统中使用的共享库&#xff08;Shared Library&#xff09;。与静态库不同&#xff0c;动态库…

结构型设计模式之外观模式【设计模式系列】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everythi…

雷达信号处理自学总结(持续更新)

傅里叶变换的频率分辨率 频率分辨率 采样频率 信号长度 频率分辨率 \frac{采样频率 }{信号长度} 频率分辨率信号长度采样频率​ 可用numpy模块的fft.fftfreq函数求出傅里叶变换的频率分辨率。 https://numpy.org/doc/stable/reference/generated/numpy.fft.fftfreq.html

若依vue -【 44】

44 服务监控讲解 1 需求 显示CPU、内存、服务器信息、Java虚拟机信息、磁盘状态的信息 2 前端 RuoYi-Vue\ruoyi-ui\src\views\monitor\server\index.vue <script> import { getServer } from "/api/monitor/server";export default {name: "Server&quo…