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软件设计模式中的一种结构型设计模式,主要用于解决在不改变现有对象结构的情况下动态地给对象添加新功能的问题。通过使用装饰器模式,可以在运行时为对象添加新的行为,而不需要修改其…...
Spring Boot项目接收前端参数的11种方式
大家好,我是袁庭新。在前后端项目交互中,前端传递的数据可以通过HTTP请求发送到后端, 后端在Spring Boot中如何接收各种复杂的前端数据呢?这篇文章总结了11种在Spring Boot中接收前端数据的方式。 1 搭建项目 1.通过Spring Init…...
通过GRE协议组建VPN网络
GRE(Generic Routing Encapsulation,通用路由封装协议)协议是一种简单而有效的封装协议,它在网络中的广泛应用,比如在构建VPN网络。 GRE是一种封装协议,它允许网络层协议(如IP)的…...
you-get使用cookies下载B站视频
B站视频更换BV号以后,使用you-get不能下载了。 首先更新你的you-get pip install --upgrade you-get 更新完成后再次使用you-get -u 命令会显示使用cookies才能下载更多清晰度的视频 使用Edge浏览器,添加插件 Cookie-Editor 点击上图的导出按钮&am…...
使用Excel制作通达信自定义“序列数据“
序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法:删掉没有用的数据(行与列)和股代码格式处理,是和外部数据的制作方法是相同,自己上面看历史博文。只需要判断一下,股代码跟随的…...
基于 Nginx 的网站服务器与 LNMP 平台搭建指南
一,Nginx概述 (一)Nginx的作用 Nginx在网络服务器架构中扮演着多面的角色。其初始设定专注于静态网络数据的处理,能高效地为用户提供诸如HTML,CSS,JavaScript等静态资源。当面对动态数据时,借助php - fpm模块,Nginx能够解析php源代码,实现动态页面的生成与展示。在处理…...
OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)
上一篇文章:OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理 添加椒盐噪声 def add_peppersalt_noise(image, n10000):result image.copy()h, w image.shape[:2] # 获取图片的高和宽for i in range(n): # 生成n个椒盐噪声x np.random.randint(…...
WPF自定义窗口 输入验证不生效
WPF自定义窗口 输入验证不生效 WPF ValidationRule 不生效 WPF ValidationRule 不生效 解决方案:在WindowStyle的Template中添加AdornerDecorator标签。 <Style x:Key"WindowStyle1" TargetType"{x:Type Window}"><Setter Property&…...
【MySQL】 SQL优化讲解
一、优化前的思考 在定位到慢查询后,面试官常问如何优化或分析慢查询的SQL语句。若存在聚合查询、多表查询,可尝试优化SQL语句结构,如多表查询可新增临时表;若表数据量过大,可添加索引,但添加索引后仍慢则…...
05.HTTPS的实现原理-HTTPS的握手流程(TLS1.2)
05.HTTPS的实现原理-HTTPS的握手流程(TLS1.2) 简介1. TLS握手过程概述2. TLS握手过程细化3. 主密钥(对称密钥)生成过程4. 密码规范变更 简介 主要讲述了混合加密流程完成后,客户端和服务器如何共同获得相同的对称密钥…...
Java获取自身被调用点
1. 场景 打印日志的时候,需要获取是在哪个地方被调用了,把调用点的信息一并打印出来。 2. 获取自身被调用点的方法 可以通过获取线程的调用栈,遍历后找到调用点。 3. 代码实现 import java.text.SimpleDateFormat; import java.util.Dat…...
有序之美:C++ Set的哲学与诗意
文章目录 前言一.C set 的概念1.1 set 的定义1.2 set 的特点二. set 的构造方法2.1 常见构造函数2.1.1 示例:不同构造方法 2.2 相关文档 三.set 的常用操作3.1 插入操作详解3.1.1 使用 insert() 插入元素3.1.2 使用 emplace() 插入元素3.1.3 插入区间元素 3.2 查找操…...
22. 仿LISP运算
题目描述 LISP语言唯一的语法就是括号要配对 形如(OP P1 P2 ...),括号内元素由单个空格分割。其中第一个元素OP为操作符,后续元素均为其参数,参数个数取决于操作符类型。注意:参数P1,P2也有可能是另外一个嵌套的(OP P1 P2...),当前…...
大模型应用技术系列(三): 深入理解大模型应用中的Cache:GPTCache
前言 无论在什么技术栈中,缓存都是比较重要的一部分。在大模型技术栈中,缓存存在于技术栈中的不同层次。本文将主要聚焦于技术栈中应用层和底层基座之间中间件层的缓存(个人定位),以开源项目GPTCache(LLM的语义缓存)为例,深入讲解这部分缓存的结构和关键实现。 完整技术…...
MATLAB语言的网络编程
标题:MATLAB中的网络编程:深入探索与实践 一、引言 在现代科学和工程领域中,网络编程已经成为了数据处理、信号分析、模型构建等众多任务中不可或缺的一环。MATLAB作为一款强大的数学计算软件,不仅提供了丰富的数值计算功能&…...
边缘计算收益稳定
要使自己的PCDN(Personal Content Delivery Network,个人内容分发网络)收益更稳定,可以从以下几个方面进行努力: 一、选择合适的PCDN平台 平台稳定性:选择技术成熟、稳定性高的PCDN平台,确保内…...
计算机网络 (7)物理层下面的传输媒体
一、定义与位置 物理层是计算机网络体系结构的最低层,它位于传输媒体(传输介质)之上,主要作用是为数据链路层提供一个原始比特流的物理连接。这里的“比特流”是指数据以一个个0或1的二进制代码形式表示。物理层并不是特指某种传输…...
【GoPL】1.2 命令行参数
1.2 命令行参数 24-12-26 大部分程序处理输入,然后产生一些输出,这大概有点像计算的定义 但是程序怎么操作输入的数据?(用参数来操作)输入可能来自文件、网络连接、用户的键盘输入、命令行参数(不同的编程范式) os包提供函数和其他值来处理…...
高精度问题
目录 算法实现基础 高精度加法AB 测试链接 源代码 代码重点 高精度减法A-B 测试链接 源代码 代码重点 高精度乘法A*b和A*B 测试链接 源代码 代码重点 高精度除法A/b和A/B 测试链接 源代码 代码重点 高精度求和差积商余 算法实现基础 本算法调用STL…...
【无线通信】蜂窝系统——干扰与系统容量
干扰是蜂窝无线系统性能的主要限制因素。干扰来源包括同一小区中的其他移动终端、邻近小区正在进行的通话、其他基站在同一频段内的工作信号,或者任何不属于蜂窝系统的设备偶然向蜂窝频段泄漏信号。语音信道中的干扰会导致串音,使得用户在通话时听到背景…...
深入探索仓颉编程语言:函数与结构类型的终极指南
引言 仓颉编程语言是一种现代化、语法精炼的编程语言,其设计目标是提供高度的灵活性与高性能的执行效率。函数与结构类型是仓颉语言的两大基础模块,也是开发者需要掌握的核心。本文将详细讲解仓颉语言中函数和结构类型的特性,辅以代码实例和…...
010-spring-后置处理器(重要)
org.mybatis.spring.mapper.MapperScannerConfigurer...
SQL实现新年倒计时功能
马上就到 2025 年了,给大家分享一个使用 SQL 实现的新年倒计时功能。 以下是 PostgreSQL 语法: DO $$ DECLAREdiff INTERVAL; BEGINRAISE NOTICE 2025新年倒计时开始:;LOOP-- 计算当前时间距离2025年的时间间隔diff age(timestamp 2025-01…...
list模拟实现
目录 节点结构 构造函数 insert erase push_back push_front pop_front pop_back 拷贝构造 析构函数 赋值重载 正向迭代器实现 clear 反向迭代器实现 测试list 附完整代码 参照数据结构篇: 带头双向循环链表 节点结构 namespace dck {template <class T&g…...
JVM【Java虚拟机】基础知识(五)
1. 双亲委派机制 由于Java虚拟机中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题。 💡双亲委派机制有什么用? 1.保证类加载的安全性 通过双亲委派机制避免恶意代码替换JDK中的核心类库,比如java.lang.Str…...
阿尔萨斯(JVisualVM)JVM监控工具
文章目录 前言阿尔萨斯(JVisualVM)JVM监控工具1. 阿尔萨斯的功能2. JVisualVM启动3. 使用 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 而且听说点赞的人每天的运气都不会太差ÿ…...
Vue BPMN Modeler流程图
1、参考地址 git clone https://github.com/evanyangg/vue-bpmn-modeler.git 2、安装bpmn.js npm install bpmn-js --save 3、使用bpmn.js <template><div class"containers"><div class"canvas" ref"canvas"></div&g…...
python通过正则匹配SQL
pattern r"(?:[^;]|(?:\\.|[^])*);" sql_list [match.group().strip() for match in re.finditer(pattern, execute_sql) if match.group().strip()]for sql in sql_list:print(sql)(?:[^;]|(?:\\.|[^])*); 匹配 连续的非分号内容 或 单引号包裹的字符串&#…...
设置首选网络类型以及调用Android框架层的隐藏API
在Android SDK中提供的framework.jar是阉割版本的,比如有些类标记为hide,这些类不会被打包到这个jar中,而有些只是类中的某个方法或或属性被标记为hide,则这些类或属性会被打包到framework.jar,但是我们无法调用&#…...
观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?
大家好,我是锋哥。今天分享关于【观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?】面试题。希望对大家有帮助; 观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用? 1000道 …...
如何保证mysql数据库到ES的数据一致性
1.同步双写方案 在代码中对数据库和ES进行双写操作,确保先更新数据后更新ES。 优点: 数据一致性:双写策略可以保证在MySql和Elasticsearch之间数据的强一致性,因为每次数据库的变更都会在Elasticsearch同步反映。实时性…...
RabbitMQ 的7种工作模式
RabbitMQ 共提供了7种⼯作模式,进⾏消息传递,. 官⽅⽂档:RabbitMQ Tutorials | RabbitMQ 1.Simple(简单模式) P:⽣产者,也就是要发送消息的程序 C:消费者,消息的接收者 Queue:消息队列,图中⻩⾊背景部分.类似⼀个邮箱,可以缓存消息;⽣产者向其中投递消息,消费者从其中取出消息…...
红黑树 Red-Black Tree介绍
1. 红黑树的定义 红黑树是一种具有如下性质的二叉搜索树: 每个节点是红色或黑色。根节点是黑色。所有叶子节点都是黑色的空节点(NIL节点),即哨兵节点。如果一个节点是红色,那么它的子节点一定是黑色。(不存…...
我的创作纪念日—致敬未来的自己
机缘 为什么想去写文章呢? 1、想把自己学的知识和技能做一个总结。 2、想给多年后的自己留下一些财富。 3、希望自己分享的知识和经验也能帮到其他有需要的人 收获 在创作的过程中都有哪些收获? 1、每次对知识的总结,都让我的技能更加的…...
Android Studio IDE环境配置
需要安装哪些东西: Java jdk Java Downloads | OracleAndroid Studio 下载 Android Studio 和应用工具 - Android 开发者 | Android DevelopersAndroid Sdk 现在的Android Studio版本安装时会自动安装,需要注意下安装的路径Android Studio插件…...
matlab中的cell
在MATLAB中,cell 是一种非常重要的数据类型,它能够存储不同类型和大小的数据,这使得它非常灵活,适用于处理复杂的数据结构。 1. 基本介绍 cell 类型的变量可以存储不同类型的数据,如数值、字符、结构体、甚至其他的 …...
Vue项目中env文件的作用和配置
在实际项目的开发中,我们一般会经历项目的开发阶段、测试阶段和最终上线阶段,每一个阶段对于项目代码的要求可能都不尽相同,那么我们如何能够游刃有余的在不同阶段下使我们的项目呈现不同的效果,使用不同的功能呢?这里…...
基于致远OA+慧集通平台的企业主数据管理设计方案(一)
目标 1、实现集团组织主数据的集中统一管理,包括到主数据在致远中的审批新增、编辑、分发等操作; 2、实现集团用户系统权限的集中管理,统一在致远平台中为用户配置各系统中的权限,配置完成后,可以自动或手动的分发到…...
vue前端实现同步发送请求,可设置并发数量【已封装】
新建 TaskManager.js export default class TaskManager {constructor(maxConcurrentTasks 1) {// 最大并发任务数// to do// 并发任务数大于1 接口开始有概率返回空值,推测是后端问题this.maxConcurrentTasks maxConcurrentTasks;this.currentTasks 0;this.tas…...
vue3使用vant日历组件(calendar),自定义日历下标的两种方法
在vue3中使用vant日历组件(calendar)自定义下标的两种方法,推荐使用第二种: 日期下方加小圆点: 一、使用伪元素样式实现(::after伪元素小圆点样式会被覆盖,只能添加一个小圆点) 代码如下(示例…...
Java线程池面试题
为什么要用线程池 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行方便管理线程:线程是稀缺资源,如果无条件地创建࿰…...
我的 2024 年终总结
2024 年,我离开了待了两年的互联网公司,来到了一家聚焦教育机器人和激光切割机的公司,没错,是一家硬件公司,从未接触过的领域,但这还不是我今年最重要的里程碑事件 5 月份的时候,正式提出了离职…...
Mysql8 数据库安装及主从配置
一、MySQL8 安装 下载 MySQL 8 的安装包并将其上传到服务器。将安装包解压到指定的目录,例如 /opt/mysql8。创建一个名为 mysql 的用户组和一个名为 mysql 的用户,并将用户添加到组中。同时,设置用户密码并更改用户的主目录和默认 shell。配…...
Unity中UGUI的Button动态绑定引用问题
Unity中UGUI的Button动态绑定引用问题 问题代码修改代码如下总结 问题代码 Button动态绑定几个连续的按钮事件时使用for循环的i做按钮的id发现按钮点击对应不上。如下代码 for (int i 0; i < 10; i) {btn[i].onClick.AddListener(() >{Click(i);}); }/// <summary&…...
测试基础之测试分类
软件测试是确保软件产品满足预期功能、性能和用户体验要求的关键环节。它的主要目的是通过系统化的方法发现并修复软件中的缺陷,从而提高软件的质量和可靠性。在软件开发生命周期的不同阶段执行测试,以尽早发现潜在的错误或类型,早期发现缺陷…...
VS2022 中的 /MT /MTd /MD /MDd 选项
我们有时编译时,需要配置这个 运行库,指定C/C++运行时库的链接方式。 如下图 那么这些选项的含义是什么? /MT:静态链接多线程库 /MT选项代表“Multi-threaded Static”,即多线程静态库。选择此选项时,编译器会从运行时库中选择多线程静态连接库来解释程序中的代码,…...
socket.io
import { ref } from "vue" import io from "socket.io-client" import { getToken } from "./auth" const socket ref(null) const serverUri import.meta.env.VITE_APP_API_URL// 你的服务器地址 // const serverUri "http://172.16.3…...
latex常见问题汇总
文章目录 单行多图显示双栏插入图片 单行多图显示 \begin{figure}[t!] % case 1\centering\setlength{\tabcolsep}{0.5pt} % 图片之间的距离为0.5 point\begin{tabular}{ccc}\includegraphics[width0.30\linewidth, height0.33\linewidth]{pic/xuLun/thin.png} &\includeg…...
从数据到决策:如何利用多维度交叉分析提升企业整体效能
随着“GenAI”技术的崛起,数据分析在各行各业的应用也发生了深远的变化。IDC中国的调研数据显示,68%的企业在落地GenAI应用时认为,梳理和整合内部数据资产是首要任务;66%的企业则表示,搭建数据湖等数据底座是推动智能化…...
Nmap基础入门及常用命令汇总
Nmap基础入门 免责声明:本文单纯分享技术,请大家使用过程中遵守法律法规~ 介绍及安装 nmap是网络扫描和主机检测的工具。作为一个渗透测试人员,必不可少的就是获取信息。那么nmap就是我们从互联网上获取信息的途径,我们可以扫描互…...