当前位置: 首页 > news >正文

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

享元模式由以下几个组成部分构成:

  1. 享元接口(Flyweight Interface):定义了享元对象的外部状态相关方法,通过这些方法可以传递外部状态给享元对象。

  2. 具体享元类(Concrete Flyweight):实现了享元接口,负责实现并处理外部状态的具体逻辑。具体享元类可以被多个客户端共享。

  3. 享元工厂类(Flyweight Factory):负责创建和管理享元对象,它维护一个享元池(或称为对象池),用于存储已经创建的享元对象。享元工厂类提供了获取享元对象的方法,以供客户端使用。当客户端请求一个享元对象时,享元工厂类首先查找是否已经创建了符合要求的享元对象,如果存在就直接返回;如果不存在,则创建新的享元对象并加入享元池中,以备下次使用。

  4. 客户端(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

当我们考虑使用享元模式时,通常是为了解决以下两个问题:对象的数量太大,导致内存消耗过大;对象的属性可以被拆分为内部状态和外部状态,且内部状态可以共享

下面是一些常见的享元模式的应用场景:

  1. 文字编辑器:在一个文字编辑器中,每个字符都是一个对象,如果为每个字符都创建一个独立的对象会消耗大量的内存。使用享元模式可以将相同字符的对象共享,并仅保存字符的内部状态(例如字符的字体、大小),以减少内存的使用。

  2. 地图和游戏中的图块:在地图或游戏中,可能有许多相同的图块,例如草地、石头等。使用享元模式可以共享这些图块对象,并在需要时通过传递外部状态来进行定位和绘制。

  3. 缓存管理:缓存是一种常见的使用享元模式的场景。比如,数据库查询结果的缓存,如果结果对象已经存在于缓存中,则直接使用共享的对象,避免重复查询和创建新对象。

  4. 线程池:在多线程的应用中,为每个任务创建与之对应的线程开销很大。使用享元模式可以维护一个线程池,并在需要时共享线程对象,提高性能和资源利用率。

  5. 网络连接池:在网络编程中,创建和销毁网络连接也需要消耗较多的资源。通过使用享元模式,可以维护一个连接池,并共享可用的连接对象,以减少连接的创建和销毁次数,提高系统的性能和可扩展性。

需要注意的是,享元模式并不适用于所有情况。只有当对象的内部状态可以共享,并且外部状态可以通过参数传递时,才适合使用享元模式。在一些场景下,由于复杂性和维护成本的问题,可能选择不使用享元模式。因此,在具体应用时需仔细考虑使用场景和模式的适配性。

相关文章:

AI Chat 设计模式:12. 享元模式

本文是该系列的第十二篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 给我介绍一下享元模式A.1Q.2 也就是说&#xff0c;其实共享的是对象的内部状态&…...

Spring mvc:SpringServletContainerInitializer

SpringServletContainerInitializer实现了Servlet3.0规范中定义的ServletContainerInitializer&#xff1a; public interface ServletContainerInitializer {void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; }SpringServletCont…...

微信小程序中的全局数据共享(状态管理)使用介绍

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

LLM - LLama 模型读取报错 TypeError: not a string

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

2023年08月在线IDE流行度最新排名

点击查看最新在线IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年08月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多&#xff0c;人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…...

k8s的架构

简介 一个 K8s 系统&#xff0c;通常称为一个 K8s 集群&#xff0c;集群主要包括两个部分 一个 Master 节点&#xff08;主节点&#xff09; 一群 Node 节点&#xff08;计算节点&#xff09; 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、弱化次要元素 对于可以弱化的元素&#xff0c…...

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前端转正工作总结&#xff08;篇1&#xff09; 来到__有限公司已经三个月了&#xff0c;目前的工作是前端开发&#xff0c;我是一名应届毕业生&#xff0c;之前没有过工作经验&#xff0c;在刚来到__这个大家庭的时候&#xff0c;我就被这里的工作气氛深深地吸引&#xff0…...

P1144 最短路计数

最短路计数 题目描述 给出一个 N N N 个顶点 M M M 条边的无向无权图&#xff0c;顶点编号为 1 ∼ N 1\sim N 1∼N。问从顶点 1 1 1 开始&#xff0c;到其他每个点的最短路有几条。 输入格式 第一行包含 2 2 2 个正整数 N , M N,M N,M&#xff0c;为图的顶点数与边数…...

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 安井食品集团股份有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1.负责集团组网的网络规划、实施、维护工作&#xff1b; 2.负责公司局域网的网络规划、实施、维护工作&#xff1b; 3.负责公司企业安全系统规划、实施、维护工作&#xff1b; 4、负责公…...

小程序商品如何设置限购

限购是一种常用的小程序商品销售策略&#xff0c;可以帮助商家提高销售额、控制库存和增加用户的购买欲望。那么&#xff0c;小程序产品怎么设置限购呢&#xff1f;下面将为您详细介绍。 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 &#xff08;Message Queuing Telemetry Transport&#xff09;消息队列遥测传输协议&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的"轻量级"通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff0c;由IBM在1999年发布。M…...

操作系统专栏4-网络专题from小林coding

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

《C和指针》(6)指针

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

零基础强化学习入门分享

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

springboot 百货中心供应链管理系统小程序

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

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...