AI Chat 设计模式:12. 享元模式
本文是该系列的第十二篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。
问题列表
- Q.1 给我介绍一下享元模式
- A.1
- Q.2 也就是说,其实共享的是对象的内部状态,外部状态还是要多次创建的吧
- A.2
- Q.3 明白,细说一下享元模式的组成吧
- A.3
- Q.4 举一个享元模式的例子吧,使用c++
- A.4
- Q.5 物理学中有说“同一个质点不可能在同一时刻位于不同的空间中”,那同一个矩形是如何在不同的位置上显示出来的呢?
- A.5
- Q.6 那把你前面举得享元模式的例子再完善一下,体现出是如何把同一个矩形绘制到不同的位置,并赋予了不同的颜色
- A.6
- Q.7 我大概明白了,请继续给我举一些享元模式的应用场景,帮我加深理解,不用给出具体代码
- A.7
Q.1 给我介绍一下享元模式
A.1
享元模式是一种软件设计模式,用于提高系统的性能和内存利用率。该模式通过共享对象来减少内存消耗,特别是在大量对象需要同时存在的情况下。
在享元模式中,对象分为两种:内部状态
和外部状态
。内部状态是不随外部环境变化而变化的状态,可以被多个对象共享;而外部状态则是随外部环境变化而变化的状态,每个对象都有自己的外部状态。
具体而言,享元模式将对象的创建和管理分为两部分:共享的部分和独立的部分。共享的部分被封装在享元对象中,可以被多个具体对象共享,而独立的部分则由客户端管理。当需要创建对象时,客户端首先通过工厂类获取享元对象,然后向享元对象传递该对象的外部状态。享元对象根据外部状态进行处理,并返回结果给客户端。
通过使用享元模式,系统可以减少内存占用,提高性能。因为多个对象共享内部状态,所以需要的内存比创建每个对象的内存总和要小。同时,由于外部状态由客户端管理,所以可以更灵活地操作对象,不同的对象可以共享相同的内部状态,提高了系统的可扩展性。
总结一下,享元模式通过共享对象的方式来减少内存消耗,在某些情况下可以显著提高系统的性能和内存利用率。它适用于有大量相似对象需要同时存在的场景,如文本编辑器中的字符对象、网页中的图片对象等。
享元,也即共享单元、共享对象。
目的:减少内存消耗。享元的英文名叫 Flyweight,轻量级的意思,就体现出这一点。
手段:把对象分为内部状态和外部状态,内部状态共享,外部状态随外部环境变化。
Q.2 也就是说,其实共享的是对象的内部状态,外部状态还是要多次创建的吧
A.2
是的,你理解得很对。在享元模式中,对象的内部状态是可以被共享的,而外部状态是需要根据需要多次创建的。
具体来说,内部状态是对象的固有属性,它不受外部环境的影响,因此可以被多个对象共享。而外部状态是随着外部环境的变化而变化的属性,每个对象都会有自己独立的外部状态。
当使用享元模式时,我们会将对象的内部状态封装在享元对象中,以实现共享。客户端在创建对象时会将外部状态传递给享元对象,享元对象基于这个外部状态进行处理,并返回结果给客户端。因此,每个对象在使用时都会有自己独立的外部状态。
通过共享内部状态,我们可以大幅度减少内存占用,因为多个对象可以共享相同的内部状态对象。而外部状态则根据需要传递给享元对象,以创建具有不同外部状态的对象。这样既能提高系统性能,又能灵活地处理不同的外部状态。
需要注意的是,在使用享元模式时,对于外部状态的管理是由客户端来负责的,而不是由享元对象来管理。享元对象只关心内部状态的共享和操作,具体的外部状态管理由客户端决定。这样可以使系统更加灵活和可扩展。
来个比喻,就好像活字印刷术吧。每个汉字就一个模子(享元),每个字的字形、大小是固定的,也就是其内部状态,往外拓印的时候,加上不同的染料(外部状态),就能制造出很多个各种颜色的字。这就是享元模式的思想。
Q.3 明白,细说一下享元模式的组成吧
A.3
享元模式由以下几个组成部分构成:
-
享元接口(Flyweight Interface):定义了享元对象的外部状态相关方法,通过这些方法可以传递外部状态给享元对象。
-
具体享元类(Concrete Flyweight):实现了享元接口,负责实现并处理外部状态的具体逻辑。具体享元类可以被多个客户端共享。
-
享元工厂类(Flyweight Factory):负责创建和管理享元对象,它维护一个享元池(或称为对象池),用于存储已经创建的享元对象。享元工厂类提供了获取享元对象的方法,以供客户端使用。当客户端请求一个享元对象时,享元工厂类首先查找是否已经创建了符合要求的享元对象,如果存在就直接返回;如果不存在,则创建新的享元对象并加入享元池中,以备下次使用。
-
客户端(Client):通过享元工厂类获取享元对象,并根据需要传递外部状态给享元对象进行操作。客户端负责维护和管理享元对象的外部状态。
以上是享元模式的主要组成部分。通过使用享元模式,可以有效地减少内存消耗,提高系统性能和资源利用率,尤其在大量相似对象需要同时存在的情况下尤为适用。
享元接口定义外部状态相关方法,享元工厂创建和管理享元对象(内部状态)。
Q.4 举一个享元模式的例子吧,使用c++
A.4
当我们绘制一个含有大量相同形状的二维图形时,可以使用享元模式来减少内存消耗。下面是一个使用C++实现享元模式的简单例子:
首先,我们定义一个简单的图形类 Shape,它有两个属性:形状类型和颜色。其中形状类型是内部状态,颜色是外部状态
。
class Shape {
public:virtual void draw(const std::string& color) = 0;
};
然后,我们创建具体的图形类 Rectangle,用于绘制矩形形状。
class Rectangle : public Shape {
public:void draw(const std::string& color) override {std::cout << "Drawing a rectangle with color " << color << std::endl;}
};
draw 这个接口含义一个参数,用来接收外部状态,颜色。
接下来,我们实现享元工厂类 ShapeFactory,用于创建和管理享元对象。
class ShapeFactory {
private:std::map<std::string, Shape*> shapePool;public:Shape* getShape(const std::string& type) {Shape* shape = shapePool[type];if (!shape) {if (type == "rectangle") {shape = new Rectangle();shapePool[type] = shape;}// 可以添加更多的具体图形类}return shape;}
};
工厂用户获取不同形状,也就是不同的元。
最后,我们可以在客户端代码中使用享元模式来绘制多个形状。
int main() {ShapeFactory shapeFactory;Shape* shape1 = shapeFactory.getShape("rectangle");Shape* shape2 = shapeFactory.getShape("rectangle");shape1->draw("red"); // 绘制一个红色的矩形shape2->draw("blue"); // 绘制一个蓝色的矩形// 注意:shape1和shape2指向的是同一个矩形对象,因为矩形是可以共享的delete shape1;delete shape2;return 0;
}
在上面的例子中,ShapeFactory
维护着一个享元池(shapePool
),在获取图形对象时,首先从池中查找是否已经存在符合要求的对象,如果存在则直接返回;如果不存在,则创建一个新的对象并将其添加到享元池中。通过共享矩形对象,我们可以减少内存消耗。
请注意,这只是一个简单的示例,实际使用时还需要考虑对象的生命周期管理、线程安全性等问题。
Q.5 物理学中有说“同一个质点不可能在同一时刻位于不同的空间中”,那同一个矩形是如何在不同的位置上显示出来的呢?
A.5
物理学中的“同一个质点不可能在同一时刻位于不同的空间中”是指在经典物理学中,一个物体在某一时刻只能存在于一个确定的位置。这是因为经典物理学中的物体被认为是具有确定位置和确定动量的,即满足粒子本质的局域性。
然而,在计算机图形学中的矩形绘制中,并不是通过物理定律来模拟的。计算机图形学是一门应用数学和计算机科学的学科,它的目标是通过计算机算法和技术来生成和处理图像。
当我们在计算机图形学中绘制一个矩形时,实际上是使用数学坐标系来描述矩形的位置和形状。这些坐标系是虚拟的,并不是物理空间中的真实位置。计算机图形学中的物体是通过顶点和像素来表示的。
对于一个矩形来说,我们可以通过设置矩形的顶点坐标(如左上角和右下角)来确定其位置。无论在屏幕的哪个位置绘制矩形,都是通过计算机图形学的算法将矩形的顶点映射到屏幕上的像素点。这些像素点实际上是屏幕上的具体位置,而不是物理空间中的位置。
因此,同一个矩形在不同的位置上显示,是通过计算机图形学中的算法和技术来实现的,并不违反物理学中的局域性原理。
还记得活字印刷术吗?
Q.6 那把你前面举得享元模式的例子再完善一下,体现出是如何把同一个矩形绘制到不同的位置,并赋予了不同的颜色
A.6
好的,让我们再细化前面的享元模式的例子,以展示如何绘制同一个矩形到不同的位置,并为其赋予不同的颜色。
首先,我们更新 Shape 接口,使其可以传递位置信息和颜色信息。
class Shape {
public:virtual void draw(const std::string& color, int x, int y) = 0;
};
现在有位置、颜色两个外部状态,draw() 方法有位置、颜色两个参数。当然,不一定是新增参数,也可以通过其他方式来接收外部状态。
接下来,我们更新具体的图形类 Rectangle,使其可以接收并使用位置和颜色信息。
class Rectangle : public Shape {
public:void draw(const std::string& color, int x, int y) override {std::cout << "Drawing a rectangle at position (" << x << ", " << y << ") with color " << color << std::endl;}
};
然后,我们更新享元工厂类 ShapeFactory,使其能够传递位置信息给矩形对象并在指定位置进行绘制。
class ShapeFactory {
private:std::map<std::string, Shape*> shapePool;public:Shape* getShape(const std::string& type) {Shape* shape = shapePool[type];if (!shape) {if (type == "rectangle") {shape = new Rectangle();shapePool[type] = shape;}// 可以添加更多的具体图形类}return shape;}void drawShape(const std::string& type, const std::string& color, int x, int y) {Shape* shape = getShape(type);shape->draw(color, x, y);}
};
最后,在客户端代码中,我们可以通过享元工厂类指定矩形的位置和颜色进行绘制。
int main() {ShapeFactory shapeFactory;shapeFactory.drawShape("rectangle", "red", 10, 20); // 绘制一个红色的矩形在位置 (10, 20)shapeFactory.drawShape("rectangle", "blue", 50, 30); // 绘制一个蓝色的矩形在位置 (50, 30)shapeFactory.drawShape("rectangle", "green", 100, 50); // 绘制一个绿色的矩形在位置 (100, 50)return 0;
}
在上述代码中,我们通过调用 ShapeFactory 的 drawShape 方法,传递了不同的颜色和位置参数,从而绘制了同一个矩形到不同的位置,并赋予了不同的颜色。
请注意,这只是一个简单的示例,实际使用时我们还需要考虑更多的绘制参数和逻辑。但核心的思想是,通过维护共享的矩形对象以及传递外部状态信息,我们可以在不同的位置上绘制出同一个矩形,并赋予不同的颜色。这样,我们可以在节约内存的同时得到多样化的绘制结果。
同样看一下类图。这里的 OtherShape 指三角形、圆形等其他图形。
draw 方法提供外部状态的传入口。
ShapeFactory 中的 getShape 用于获取不同的元(不同的形状对象)。
Q.7 我大概明白了,请继续给我举一些享元模式的应用场景,帮我加深理解,不用给出具体代码
A.7
当我们考虑使用享元模式时,通常是为了解决以下两个问题:对象的数量太大,导致内存消耗过大;对象的属性可以被拆分为内部状态和外部状态,且内部状态可以共享
。
下面是一些常见的享元模式的应用场景:
-
文字编辑器:在一个文字编辑器中,每个字符都是一个对象,如果为每个字符都创建一个独立的对象会消耗大量的内存。使用享元模式可以将相同字符的对象共享,并仅保存字符的内部状态(例如字符的字体、大小),以减少内存的使用。
-
地图和游戏中的图块:在地图或游戏中,可能有许多相同的图块,例如草地、石头等。使用享元模式可以共享这些图块对象,并在需要时通过传递外部状态来进行定位和绘制。
-
缓存管理:缓存是一种常见的使用享元模式的场景。比如,数据库查询结果的缓存,如果结果对象已经存在于缓存中,则直接使用共享的对象,避免重复查询和创建新对象。
-
线程池:在多线程的应用中,为每个任务创建与之对应的线程开销很大。使用享元模式可以维护一个线程池,并在需要时共享线程对象,提高性能和资源利用率。
-
网络连接池:在网络编程中,创建和销毁网络连接也需要消耗较多的资源。通过使用享元模式,可以维护一个连接池,并共享可用的连接对象,以减少连接的创建和销毁次数,提高系统的性能和可扩展性。
需要注意的是,享元模式并不适用于所有情况。只有当对象的内部状态可以共享,并且外部状态可以通过参数传递时,才适合使用享元模式。在一些场景下,由于复杂性和维护成本的问题,可能选择不使用享元模式。因此,在具体应用时需仔细考虑使用场景和模式的适配性。
相关文章:

AI Chat 设计模式:12. 享元模式
本文是该系列的第十二篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 给我介绍一下享元模式A.1Q.2 也就是说,其实共享的是对象的内部状态&…...
Spring mvc:SpringServletContainerInitializer
SpringServletContainerInitializer实现了Servlet3.0规范中定义的ServletContainerInitializer: public interface ServletContainerInitializer {void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; }SpringServletCont…...

微信小程序中的全局数据共享(状态管理)使用介绍
开发工具:微信开发者工具Stable 1.06 一、状态管理简介 微信小程序全局状态是指可以在不同页面之间共享的数据或状态。 它可以存储用户的登录状态、个人信息、全局配置信息等。 二、安装MobX 1、安装NPM 在资源管理器的空白地方点右键,选择“在外部…...

LLM - LLama 模型读取报错 TypeError: not a string
一.引言 读取 LLama2 模型时报错 TypeError: not a string 看异常栈是 AutoTokenizer.from_pretrained 时候的异常。 二.问题解决 出现类似加载模型异常的问题,大致分两类: ◆ 模型地址异常 脚本里传的 pretrained_model Path 有问题,加 …...

2023年08月在线IDE流行度最新排名
点击查看最新在线IDE流行度最新排名(每月更新) 2023年08月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多,人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…...
k8s的架构
简介 一个 K8s 系统,通常称为一个 K8s 集群,集群主要包括两个部分 一个 Master 节点(主节点) 一群 Node 节点(计算节点) Master节点 Master 节点包括 API Server、Scheduler、Controller manager、etcd A…...

数据分析基础-Excel图表的美化操作(按照教程一步步操作)
一、原始数据 包含月份和对应的销量和产量。 时间销量产量1月60722月38673月28344月58685月67596月72357月61428月24319月556710月243511月122112月2645 二、原始的图表设计-采用Excel自带模板 三、优化思路 1、删除多余元素 2、弱化次要元素 对于可以弱化的元素,…...
Mybatis嵌套查询(一对多)
一、返回数据Java类 Data public class PersonnelDetailsVO{/*** 主键*/Column(name "ID", length 36, precision 0)private String id;/*** 人员姓名*/Column(name "OPERATE_NAME", length 36, precision 0)private String operateName;/*** 单位i…...

web前端转正工作总结范文5篇
web前端转正工作总结(篇1) 来到__有限公司已经三个月了,目前的工作是前端开发,我是一名应届毕业生,之前没有过工作经验,在刚来到__这个大家庭的时候,我就被这里的工作气氛深深地吸引࿰…...
P1144 最短路计数
最短路计数 题目描述 给出一个 N N N 个顶点 M M M 条边的无向无权图,顶点编号为 1 ∼ N 1\sim N 1∼N。问从顶点 1 1 1 开始,到其他每个点的最短路有几条。 输入格式 第一行包含 2 2 2 个正整数 N , M N,M N,M,为图的顶点数与边数…...
Docker入门之命令
Docker命令学习方式 docker -h docker run --help # 这种形式参考 # 官方帮助 # https://docs.docker.com/reference/ Docker中命令是一等公民, 容器是为命令服务的,甚至启动容器都是为了执行一个命令 run docker run -i -t --name c1 centos:latest bash # 翻译: docker ru…...

Multimodal Learning with Transformer: A Survey
Transformer多模态学习 Abstract1 INTRODUCTION2 BACKGROUND2.1 Multimodal Learning (MML)2.2 Transformers: a Brief History and Milestones2.3 Multimodal Big Data 3 TRANSFORMERS: A GEOMETRICALLY TOPOLOGICAL PERSPECTIVE3.1 Vanilla Transformer3.1.1 Input Tokenizat…...

网工内推 | 实施、售后工程师,厂商认证优先
01 安井食品集团股份有限公司 招聘岗位:网络工程师 职责描述: 1.负责集团组网的网络规划、实施、维护工作; 2.负责公司局域网的网络规划、实施、维护工作; 3.负责公司企业安全系统规划、实施、维护工作; 4、负责公…...

小程序商品如何设置限购
限购是一种常用的小程序商品销售策略,可以帮助商家提高销售额、控制库存和增加用户的购买欲望。那么,小程序产品怎么设置限购呢?下面将为您详细介绍。 1. 设置限购数量 可以设置最低购买数量来鼓励用户批量购买或满足特定的销售需求。例如&…...
通信原理复习公式整理(自用/持续更新)
目录 符号表欧拉公式第一章平均信息量传信率(信息速率)传码率(码元速率) 第二章第三章第四章第五章第六章 数字信号的载波传输2ASK带宽余弦滚降基带信号-2ASK带宽2FSK带宽余弦滚降基带信号-2ASK带宽2PSK带宽匹配滤波器最大输出信噪比最佳线性滤波器传输特性ASK系统信号能量FSK系…...
TypeScript实战篇 - TS实战: 服务层开发 - 完整的聊天服务
目录 huatian-svc/src/main.ts huatian-svc/src/context/ChatContext.ts huatian-svc/src/ChatSession.ts huatian-svc/src/service/ChatIDService.ts huatian-svc/src/dao/DB.ts huatian-svc/src/dao/Dao.ts huatian-svc/src/dao/create_db.ts huatian-svc/src/main.ts…...

【雕爷学编程】MicroPython动手做(32)——物联网之MQTT
MQTT (Message Queuing Telemetry Transport)消息队列遥测传输协议,是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。M…...

操作系统专栏4-网络专题from小林coding
网络专题 文件传输mmapwritesend file大文件传输过程 文件传输 传统的文件传输过程 在这个过程中发生了4次用户态与内核态之间的切换,4次数据拷贝分别是 read系统调用陷入内核,read完成返回write调用陷入内核,write返回 4次数据拷贝分别是 磁盘->内核缓冲区->用户缓冲…...

《C和指针》(6)指针
1、内存和地址 计算机的内存是由数以亿万计的位(bit)组成,每一个位可以容纳值0、1值。由于一个位所能表示的值的范围太有限,所以单独的位用处不大。通常许多为合成一组作为一个单位,这样就可以存储范围较大的值。下图…...

零基础强化学习入门分享
(一)前言:强化学习入门顺序。 以前主要学习硬件PCB单片机等知识,后来接触的项目也大多与电气相关,从一窍不通到稍微找到点门道,中间走过不少弯路,误打误撞中,也留下了一些经验。 我的…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...