C++软件设计模式之装饰器模式
装饰器模式(Decorator Pattern)是C++软件设计模式中的一种结构型设计模式,主要用于解决在不改变现有对象结构的情况下动态地给对象添加新功能的问题。通过使用装饰器模式,可以在运行时为对象添加新的行为,而不需要修改其原始类或创建大量的子类。
装饰器模式的适用场合
-
动态扩展功能:当你需要在不修改现有代码的情况下,动态地为对象添加新的功能时,装饰器模式非常有用。它允许你在运行时通过组合来扩展对象的行为。
-
避免类爆炸:如果使用继承来扩展功能,可能会导致类的数量急剧增加(类爆炸问题)。装饰器模式通过组合的方式避免了这一问题,减少了类的数量。
-
遵循开闭原则:装饰器模式允许在不修改现有代码的情况下扩展功能,符合“开闭原则”(对扩展开放,对修改封闭)。
-
处理具有多个独立功能的对象:当你需要为对象添加多个独立的功能,而这些功能可以任意组合时,装饰器模式非常适用。例如,窗口系统中的边框、滚动条、阴影等功能可以通过装饰器模式动态组合。
-
框架和库的设计:在设计框架或库时,装饰器模式可以用于提供灵活的扩展机制,使用户能够根据自己的需求为对象添加功能。
装饰器模式与树结构的内在关联
装饰器模式确实与树结构存在一定的内在关联。在装饰器模式中,装饰器可以嵌套使用,形成一个链式的结构。每个装饰器可以包装另一个装饰器,最终形成一个类似树结构的层次关系。
-
链式结构:装饰器模式的核心在于通过组合的方式将多个装饰器串联起来,形成一个链式结构。每个装饰器都持有一个对被装饰对象的引用,并且可以在调用被装饰对象的方法前后添加自己的行为。这种链式结构在某种程度上类似于树的层次结构。
-
递归调用:装饰器模式中的行为可以通过递归调用来层层叠加。例如,如果你有多个装饰器,每个装饰器都会调用下一个装饰器的方法,直到最终调用到被装饰对象。这种递归调用的方式与树结构的遍历有相似之处。
-
动态组合:装饰器模式允许在运行时动态地组合多个装饰器,形成不同的功能组合。这种灵活性使得多个装饰器可以像树的分支一样,根据需要动态地组合在一起。
装饰器模式的UML类图
+-------------------+
| Component |
+-------------------+
| - operation() |
+-------------------+^|
+-------------------+
| ConcreteComponent|
+-------------------+
| - operation() |
+-------------------++-------------------+
| Decorator |
+-------------------+
| - component: Component |
| - operation() |
+-------------------+^|
+-------------------+ +-------------------+
| ConcreteDecoratorA | | ConcreteDecoratorB |
+-------------------+ +-------------------+
| - operation() | | - operation() |
+-------------------+ +-------------------+
详细说明
-
Component(组件接口):
- 定义了一个抽象接口,所有的具体组件和装饰器都必须实现这个接口。
- 方法:
operation()
- 定义了组件的基本行为。
-
ConcreteComponent(具体组件):
- 实现了Component接口,定义了具体组件的行为。
- 方法:
operation()
- 实现了具体的业务逻辑。
-
Decorator(装饰器抽象类):
- 继承自Component接口,持有一个Component类型的引用或指针,可以在运行时动态地为具体组件添加行为。
- 成员变量:
component
- 持有一个Component类型的引用或指针。 - 方法:
operation()
- 调用被装饰对象的operation()
方法,并可以在此方法前后添加新的行为。
-
ConcreteDecoratorA(具体装饰器A):
- 继承自Decorator,实现具体的装饰功能。
- 方法:
operation()
- 在调用被装饰对象的operation()
方法前后添加新的行为。
-
ConcreteDecoratorB(具体装饰器B):
- 继承自Decorator,实现具体的装饰功能。
- 方法:
operation()
- 在调用被装饰对象的operation()
方法前后添加新的行为。
示例代码
假设我们有一个为文本添加不同格式的装饰器模式实现,以下是详细的C++代码示例:
#include <iostream>
#include <string>// Component(组件接口)
class TextComponent {
public:virtual std::string getText() const = 0;virtual ~TextComponent() {}
};// ConcreteComponent(具体组件)
class PlainText : public TextComponent {
private:std::string _text;
public:PlainText(const std::string& text) : _text(text) {}std::string getText() const override {return _text;}
};// Decorator(装饰器抽象类)
class TextDecorator : public TextComponent {
protected:TextComponent* _component;
public:TextDecorator(TextComponent* component) : _component(component) {}std::string getText() const override {return _component->getText();}
};// ConcreteDecoratorA(具体装饰器A - 加粗)
class BoldText : public TextDecorator {
public:BoldText(TextComponent* component) : TextDecorator(component) {}std::string getText() const override {return "<b>" + TextDecorator::getText() + "</b>";}
};// ConcreteDecoratorB(具体装饰器B - 斜体)
class ItalicText : public TextDecorator {
public:ItalicText(TextComponent* component) : TextDecorator(component) {}std::string getText() const override {return "<i>" + TextDecorator::getText() + "</i>";}
};int main() {// 创建一个具体组件TextComponent* plainText = new PlainText("Hello, World!");// 使用具体装饰器A(加粗)TextComponent* boldText = new BoldText(plainText);// 使用具体装饰器B(斜体)TextComponent* italicBoldText = new ItalicText(boldText);// 输出装饰后的文本std::cout << italicBoldText->getText() << std::endl; // <i><b>Hello, World!</b></i>// 释放内存delete italicBoldText;delete boldText;delete plainText;return 0;
}
UML类图
为了更直观地理解,以下是装饰器模式的UML类图:
+-------------------+
| TextComponent |
+-------------------+
| - getText(): string |
+-------------------+^|
+-------------------+
| PlainText |
+-------------------+
| - _text: string |
| - getText(): string |
+-------------------++-------------------+
| TextDecorator |
+-------------------+
| - _component: TextComponent |
| - getText(): string |
+-------------------+^|
+-------------------+ +-------------------+
| BoldText | | ItalicText |
+-------------------+ +-------------------+
| - _component: TextComponent | | - _component: TextComponent |
| - getText(): string | | - getText(): string |
+-------------------+ +-------------------+
各参与者及其职责
综合应用的灵活性
通过这种方式,你可以根据需要动态地组合多个装饰器,实现多种功能的组合。例如:
-
TextComponent(组件接口):
- 职责:定义了一个抽象接口,所有的具体组件和装饰器都必须实现这个接口。
- 方法:
getText() const
- 定义了组件的基本行为。
-
PlainText(具体组件):
- 职责:实现TextComponent接口,定义了具体组件的行为。
- 方法:
getText() const
- 返回未装饰的文本。
-
TextDecorator(装饰器抽象类):
- 职责:持有一个TextComponent类型的引用或指针,可以在运行时动态地为具体组件添加行为。
- 成员变量:
_component
- 持有一个TextComponent类型的引用或指针。 - 方法:
getText() const
- 调用被装饰对象的getText()
方法,并可以在此方法前后添加新的行为。
-
BoldText(具体装饰器A):
- 职责:继承自TextDecorator,实现具体的加粗装饰功能。
- 方法:
getText() const
- 在调用被装饰对象的getText()
方法前后添加加粗的HTML标签。
-
ItalicText(具体装饰器B):
- 职责:继承自TextDecorator,实现具体的斜体装饰功能。
- 方法:
getText() const
- 在调用被装饰对象的getText()
方法前后添加斜体的HTML标签。
-
以网络传输文本为例,我们将使用装饰器模式来实现对文本的压缩、加密及其他附加功能,并展示如何通过组合这些装饰器来实现多种功能的综合应用。
1. 定义Component接口
首先,我们定义一个抽象接口
TextComponent
,该接口包含一个sendText
方法,用于表示文本传输的基本行为。#include <iostream> #include <string>class TextComponent { public:virtual ~TextComponent() {}virtual std::string sendText() const = 0; };
2. 实现ConcreteComponent
然后,我们实现一个具体的组件
PlainText
,表示未经过任何处理的普通文本。class PlainText : public TextComponent { private:std::string _text; public:PlainText(const std::string& text) : _text(text) {}std::string sendText() const override {return _text;} };
3. 定义Decorator抽象类
装饰器抽象类
TextDecorator
也继承自TextComponent
,并且持有一个TextComponent
类型的引用或指针,用于装饰的具体对象。class TextDecorator : public TextComponent { protected:TextComponent* _component; public:TextDecorator(TextComponent* component) : _component(component) {}std::string sendText() const override {return _component->sendText();} };
4. 实现具体装饰器
接下来,我们实现具体的装饰器类,分别为
CompressText
、EncryptText
和AddTimestampText
。压缩装饰器(CompressText)
class CompressText : public TextDecorator { public:CompressText(TextComponent* component) : TextDecorator(component) {}std::string sendText() const override {std::string text = TextDecorator::sendText();// 假设有一个简单的压缩算法std::string compressedText = compress(text);return compressedText;}private:std::string compress(const std::string& text) const {// 简单的压缩算法示例,这里只是返回压缩后的文本return "compress(" + text + ")";} };
加密装饰器(EncryptText)
class EncryptText : public TextDecorator { public:EncryptText(TextComponent* component) : TextDecorator(component) {}std::string sendText() const override {std::string text = TextDecorator::sendText();// 假设有一个简单的加密算法std::string encryptedText = encrypt(text);return encryptedText;}private:std::string encrypt(const std::string& text) const {// 简单的加密算法示例,这里只是返回加密后的文本return "encrypt(" + text + ")";} };
添加时间戳装饰器(AddTimestampText)
class AddTimestampText : public TextDecorator { public:AddTimestampText(TextComponent* component) : TextDecorator(component) {}std::string sendText() const override {std::string text = TextDecorator::sendText();// 假设有一个时间戳生成函数std::string timestamp = generateTimestamp();return timestamp + " " + text;}private:std::string generateTimestamp() const {// 简单的时间戳生成示例return "2024-12-24T11:34:34.221Z";} };
5. 综合应用
现在,我们可以通过组合不同的装饰器来实现多种功能的综合应用。例如,我们可以先压缩文本,再加密,最后添加时间戳。
int main() {// 创建一个具体组件TextComponent* plainText = new PlainText("Hello, World!");// 使用压缩装饰器TextComponent* compressedText = new CompressText(plainText);// 使用加密装饰器TextComponent* encryptedText = new EncryptText(compressedText);// 使用添加时间戳装饰器TextComponent* timestampedText = new AddTimestampText(encryptedText);// 输出最终处理后的文本std::cout << timestampedText->sendText() << std::endl; // 2024-12-24T11:34:34.221Z encrypt(compress(Hello, World!))// 释放内存delete timestampedText;delete encryptedText;delete compressedText;delete plainText;return 0; }
代码解释
- PlainText:这是具体组件,实现了
sendText
方法,返回未经过任何处理的普通文本。 - TextDecorator:这是装饰器抽象类,持有一个
TextComponent
类型的引用,并实现了sendText
方法,调用被装饰对象的sendText
方法。 - CompressText:实现了一个具体的压缩装饰器,调用被装饰对象的
sendText
方法后,对返回的文本进行压缩处理。 - EncryptText:实现了一个具体的加密装饰器,调用被装饰对象的
sendText
方法后,对返回的文本进行加密处理。 - AddTimestampText:实现了一个具体的添加时间戳装饰器,调用被装饰对象的
sendText
方法后,对返回的文本添加时间戳。 - 仅压缩:
new CompressText(new PlainText("Hello, World!"))
- 仅加密:
new EncryptText(new PlainText("Hello, World!"))
- 仅添加时间戳:
new AddTimestampText(new PlainText("Hello, World!"))
- 压缩和加密:
new EncryptText(new CompressText(new PlainText("Hello, World!")))
- 加密和添加时间戳:
new AddTimestampText(new EncryptText(new PlainText("Hello, World!")))
装饰器模式、策略模式和Visitor模式的相似之处和不同之处
1. 相似之处
- 动态变化:这三种模式都允许在对象创建后动态地改变其行为。
- 组合简化:它们都通过组合而不是继承来实现行为的改变,从而避免了类的爆炸。
- 行为扩展:这三种模式都提供了一种方便的方式来扩展对象的行为,而不需要修改现有的类。
2. 不同之处
装饰器模式(Decorator Pattern)
- 意图:装饰器模式旨在不改变对象接口的情况下动态地为其添加职责或行为。它通过组合的方式将多个装饰器对象串联起来,形成一个层次结构。
- 主要参与者:
- Component:定义了一个对象接口,可以动态地为其添加职责。
- ConcreteComponent:实现了Component接口,定义了具体对象的行为。
- Decorator:持有一个Component类型的引用,可以在调用被装饰对象的方法前后添加新的行为。
- ConcreteDecorator:实现了具体的装饰功能。
- 适用场合:
- 需要在不改变对象接口的情况下动态地添加功能。
- 避免使用继承导致的类爆炸问题。
- 需要多个独立的功能可以任意组合。
策略模式(Strategy Pattern)
- 意图:策略模式旨在定义一系列算法(策略),将每个算法封装起来,并使它们可以互换。客户端可以动态地选择并使用不同的策略。
- 主要参与者:
- Strategy:定义了所有支持的算法的公共接口。
- ConcreteStrategy:实现了具体的算法。
- Context:持有一个Strategy类型的引用,通过这个引用来调用策略对象的方法。
- 适用场合:
- 需要在运行时选择不同的算法或行为。
- 需要将算法的定义和使用分离,以增强代码的灵活性和可维护性。
- 有多个类似的行为需要根据不同的条件来选择。
Visitor模式(Visitor Pattern)
- 意图:Visitor模式旨在表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义新的操作。
- 主要参与者:
- Element:定义了一个接受访问者的接口,通常是一个
accept(Visitor*)
方法。 - ConcreteElement:实现了
accept(Visitor*)
方法,通常会调用访问者的访问方法。 - Visitor:定义了一个访问Element的接口,通常是一个
visit(ConcreteElement*)
方法。 - ConcreteVisitor:实现了具体的访问操作。
- ObjectStructure:可以枚举或遍历元素,提供一个高层次的接口让访问者访问元素。
- Element:定义了一个接受访问者的接口,通常是一个
- 适用场合:
- 需要在不改变对象结构的前提下,为对象结构中的元素添加新的操作。
- 有多个操作需要对对象结构中的元素进行,而且这些操作之间有共通的行为。
- 对象结构相对稳定,但需要频繁添加新的操作。
详细对比
动态变化
- 装饰器模式:通过组合多个装饰器对象动态地添加职责。
- 策略模式:通过在运行时选择不同的策略对象来改变行为。
- Visitor模式:通过在运行时选择不同的访问者对象来添加新的操作。
组合简化
- 装饰器模式:通过装饰器对象的层次结构来组合功能。
- 策略模式:通过策略对象的集合来选择和组合算法。
- Visitor模式:通过访问者对象的集合来组合操作,但不改变元素对象的结构。
行为扩展
- 装饰器模式:在对象的原有行为基础上添加新的行为,通常是在方法调用前后执行额外的操作。
- 策略模式:提供了一组可互换的算法,客户端可以在运行时选择使用哪个算法。
- Visitor模式:为对象结构中的多个元素定义新的操作,而不改变这些元素的类。
适用场合对比
装饰器模式
- 动态添加功能:最适用于在不修改对象接口的情况下动态地添加功能。
- 避免类爆炸:避免通过继承添加功能导致的类的爆炸。
- 多层次装饰:适合需要多个独立功能可以任意组合的场合。
策略模式
- 算法互换:最适合需要在运行时选择不同算法或行为的场合。
- 行为分离:适合将算法的定义和使用分离,增强代码的灵活性和可维护性。
- 条件选择:适合有多个类似行为需要根据不同条件选择的场合。
Visitor模式
- 添加新操作:最适合在不改变对象结构的前提下为对象结构中的多个元素添加新的操作。
- 稳定的对象结构:适合对象结构相对稳定,但需要频繁添加新操作的场合。
- 集中处理:适合需要集中处理多个元素的不同操作,而这些操作可能有共通部分。
总结
- 装饰器模式主要用于扩展对象的功能,通过组合而不是继承来实现动态的行为变化。
- 策略模式主要用于在运行时选择不同的算法或行为,通过策略对象的互换来实现行为的变化。
- Visitor模式主要用于在不改变对象结构的前提下为对象结构中的多个元素添加新的操作,通过访问者对象来实现新的行为。
这三种模式虽然在某些方面有相似之处,但它们的意图和适用场合各不相同,选择哪种模式取决于具体的设计需求和问题背景。
相关文章:
C++软件设计模式之装饰器模式
装饰器模式(Decorator Pattern)是C软件设计模式中的一种结构型设计模式,主要用于解决在不改变现有对象结构的情况下动态地给对象添加新功能的问题。通过使用装饰器模式,可以在运行时为对象添加新的行为,而不需要修改其…...

fpga系列 HDL:Quartus II PLL (Phase-Locked Loop) IP核 (Quartus II 18.0)
在 Quartus II 中使用 PLL (Phase-Locked Loop) 模块来将输入时钟分频或倍频,并生成多个相位偏移或频率不同的时钟信号: 1. 生成 PLL 模块 在 Quartus II 中: 打开 IP Components。 file:///C:/intelFPGA_lite/18.0/quartus/common/help/w…...
Spring AOP 中记录日志
Spring AOP 中记录日志 使用 AOP 和 Spring 提供的 RequestContextHolder 在通知中记录 HTTP 请求相关日志。以下是进阶添加日志功能的完整例子和说明。 完整示例 1. 切面类实现 Aspect Component public class LogAspect {Around("annotation(log)") // 拦截所有…...

udp tcp协议
文章目录 1. UDP协议1.1 端口号1.2 UDP协议格式1.3 UDP特性1.4 报文的封装 2. TCP协议2.1 TCP协议格式2.2 TCP策略2.2.1 确认应答机制(ACK)序号与确认序号6个标志位序号的理解 2.2.2 超时重传机制2.2.3 连接管理机制三次握手四次挥手理解三次握手理解四次挥手 2.2.4 流量控制2.…...
C语言结构体详细讲解
文章目录 [TOC] 一、前言二、结构体2.1 结构体概念🎈2.2 结构体定义🎉2.3 结构体使用🎗️ 结尾 时间紧后面还有一些知识点这周内补上, 理解理解!(❁◡❁) 一、前言 在学习结构体之前,讲讲为什么会专门写一章博客来分享…...

公交车信息管理系统:实现交通数据的智能化处理
概述 在对系统进行设计之前,需要对选题进行需求分析、可行性分析、流程分析、数据字典等内容。根据需求分析阶段,大致确定用户使用系统所需要具有的功能模块需求,由此规划出系统需要设计的相关功能模块。根据可行性分析阶段,确定系…...
在 Windows 下生成 .tgz 文件的方法
方法 1:使用 7-Zip 7-Zip 是一个流行的免费压缩工具,支持生成 .tar.gz 格式。 步骤: 下载并安装 7-Zip。准备好要压缩的文件或文件夹。右键点击文件或文件夹,选择 7-Zip > 添加到压缩文件...。在弹出的对话框中:…...

编程式浪漫,100款圣诞树代码分享
最近这几天有很多小伙伴开始寻找各种各样的圣诞树代码,我们最常用的Java,Python,C语言,前端,都是可以实现的。小巫师,就在这里分享超火的圣诞树代码,源码分享! 01.HTML圣诞树源代码…...

Nacos的下载和启动(如何快速稳定下载在github中)
目录 Nacos的下载 下载加速器 在githup中找到Nacos 启动Nacos 访问Nacos Nacos的下载 下载加速器 首先,我们需要进入githup中,我们直接访问,肯定是访问不到的。 这里我们经常玩游戏的同学肯定知道steam,这个加速器。直接进入…...
python基础知识(六)
文章目录 连接Mysql数据库安装Mysql数据库连接数据库创建数据库创建数据表查询表是否存在设置主键插入数据批量插入查询、删除、更新数据 使用PyMySql连接数据库安装PyMySql连接数据库 连接MongoDB安装pymongo驱动在MongoDB创建库及数据插入文档查询数据修改数据文档排序删除数…...

神经网络-LeNet
LeNet在1990年被提出,是一系列网络的统称,包括了LeNet1~LeNet5,对于神经网络的学习者来说,大家对下面这个图一定很熟悉,该图是对LeNet的简化展示。 在LeNet中已经提出了卷积层、Pooling层等概念,只是但是由…...
es 中 terms set 使用
在 Elasticsearch 中,terms_set 查询通常用于在一个字段上进行多值匹配,并支持设置一个条件(例如最小匹配数量),让查询结果更具灵活性。为了展示如何使用 terms_set 查询,我们首先会创建一个索引࿰…...
绩效考核试题
1.2.绩效考核 ()通过财务、客户、内部运营、学习与成长4个角度,将组织战略目标逐层分解转化为细化指标,有差异地针对不同的指标进行不同时期的绩效评估,有助于组织战略目标的实现。 A目标管理法 B平衡计分卡法 C硬性分…...
停车管理系统:构建安全、便捷的停车环境
Tomcat 简介 只要学习Java Web项目就不得不学习Tomcat。Tomcat是一种免费的开源的一种Java Web项目的容器,完美继承了 Apache服务器的特性,并且里面添加可以自动化运行的Java Web组件,让Java Web项目可以完全的运行到Tomcat里面。对于特大型项…...

十四、从0开始卷出一个新项目之瑞萨RZN2L之栈回溯(Default_Handler/hartfault)
目录 一、概述 二、参考资料 三、代码 四、日志 五、定位函数调用 六、README和工具 一、概述 软件开发中常见的比较棘手的问题就是hartfault/Default_Handler/dump,俗称跑飞了。 参考cmbacktrace,在瑞萨RZN2L/T2M实现栈回溯,串口打印…...

联通光猫怎么自己改桥接模式?
环境: 联通光猫 ZXHN F677V9 硬件版本号 V9.0 软件版本号 V9.0.0P1T3 问题描述: 联通光猫怎么自己改桥接模式 家里用的是ZXHN F677V9 光猫,最近又搞了个软路由,想改桥接模式 解决方案: 1.拿到最新超级密码&…...

突围边缘:OpenAI开源实时嵌入式API,AI触角延伸至微观世界
当OpenAI宣布开源其名为openai-realtime-embedded-sdk的实时嵌入式API时,整个科技界都为之震惊。这一举动意味着,曾经遥不可及的强大AI能力,如今可以被嵌入到像ESP32这样的微型控制器中,真正地将AI的触角延伸到了物联网和边缘计算…...

springBoot Maven 剔除无用的jar引用
目录 Used undeclared dependencies found Unused declared dependencies found 当项目经过一段时间的开发和维护后,经常会遇到项目打包速度变慢的问题。这通常与项目中包含大量的jar依赖有关,这些依赖之间的关系错综复杂。这种情况在项目维护过程中是…...

malloc 分配大堆块(128KB)的一次探索
前言 一次意外执行了 malloc(0x5000),结构使用 gdb 调试发现其分配的位置在 TLS 区域,这令我不解(:最后去看了下 malloc 源码和 mmap 源码实现,发现似乎可能是 gdb 插件的问题,乐 场景复现 #include <…...
Android -- 双屏异显之方法二
Android – 双屏异显之方法二: DisplayManager PS: 1. 使用改方法主板需连接至少两个输出显示屏; 2. 副屏内部实现与MediaRouter下一样;使用方法 # 主屏activity内: private SecondDisplay secondDisplay;private void dualScreen3288() {D…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...

算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!
今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等,设置经线、纬线都以10间隔显示。 2、需要插入背会归线…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...

Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...