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

C++设计模式(19)——访问者模式

亦称: Visitor

意图

访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。
在这里插入图片描述

问题

假如你的团队开发了一款能够使用巨型图像中地理信息的应用程序。 图像中的每个节点既能代表复杂实体 (例如一座城市), 也能代表更精细的对象 (例如工业区和旅游景点等)。 如果节点代表的真实对象之间存在公路, 那么这些节点就会相互连接。 在程序内部, 每个节点的类型都由其所属的类来表示, 每个特定的节点则是一个对象。
在这里插入图片描述
将图像导出为 XML。

一段时间后, 你接到了实现将图像导出到 XML 文件中的任务。 这些工作最初看上去非常简单。 你计划为每个节点类添加导出函数, 然后递归执行图像中每个节点的导出函数。 解决方案简单且优雅: 使用多态机制可以让导出方法的调用代码不会和具体的节点类相耦合。

但你不太走运, 系统架构师拒绝批准对已有节点类进行修改。 他认为这些代码已经是产品了, 不想冒险对其进行修改, 因为修改可能会引入潜在的缺陷。

在这里插入图片描述
所有节点的类中都必须添加导出至 XML 文件的方法, 但如果在修改代码的过程中引入了任何缺陷, 那么整个程序都会面临风险。

此外, 他还质疑在节点类中包含导出 XML 文件的代码是否有意义。 这些类的主要工作是处理地理数据。 导出 XML 文件的代码放在这里并不合适。

还有另一个原因, 那就是在此项任务完成后, 营销部门很有可能会要求程序提供导出其他类型文件的功能, 或者提出其他奇怪的要求。 这样你很可能会被迫再次修改这些重要但脆弱的类。

解决方案

访问者模式建议将新行为放入一个名为访问者的独立类中, 而不是试图将其整合到已有类中。 现在, 需要执行操作的原始对象将作为参数被传递给访问者中的方法, 让方法能访问对象所包含的一切必要数据。

如果现在该操作能在不同类的对象上执行会怎么样呢? 比如在我们的示例中, 各节点类导出 XML 文件的实际实现很可能会稍有不同。 因此, 访问者类可以定义一组 (而不是一个) 方法, 且每个方法可接收不同类型的参数, 如下所示:

class ExportVisitor implements Visitor ismethod doForCity(City c) { …… }method doForIndustry(Industry f) { …… }method doForSightSeeing(SightSeeing ss) { …… }// ……

但我们究竟应该如何调用这些方法 (尤其是在处理整个图像方面) 呢? 这些方法的签名各不相同, 因此我们不能使用多态机制。 为了可以挑选出能够处理特定对象的访问者方法, 我们需要对它的类进行检查。 这是不是听上去像个噩梦呢?

foreach (Node node in graph)if (node instanceof City)exportVisitor.doForCity((City) node)if (node instanceof Industry)exportVisitor.doForIndustry((Industry) node)// ……
}

你可能会问, 我们为什么不使用方法重载呢? 就是使用相同的方法名称, 但它们的参数不同。 不幸的是, 即使我们的编程语言 (例如 Java 和 C#) 支持重载也不行。 由于我们无法提前知晓节点对象所属的类, 所以重载机制无法执行正确的方法。 方法会将 节点基类作为输入参数的默认类型。

但是, 访问者模式可以解决这个问题。 它使用了一种名为双分派的技巧, 不使用累赘的条件语句也可下执行正确的方法。 与其让客户端来选择调用正确版本的方法, 不如将选择权委派给作为参数传递给访问者的对象。 由于该对象知晓其自身的类, 因此能更自然地在访问者中选出正确的方法。 它们会 “接收” 一个访问者并告诉其应执行的访问者方法。

// 客户端代码
foreach (Node node in graph)node.accept(exportVisitor)// 城市
class City ismethod accept(Visitor v) isv.doForCity(this)// ……// 工业区
class Industry ismethod accept(Visitor v) isv.doForIndustry(this)// ……

我承认最终还是修改了节点类, 但毕竟改动很小, 且使得我们能够在后续进一步添加行为时无需再次修改代码。

现在, 如果我们抽取出所有访问者的通用接口, 所有已有的节点都能与我们在程序中引入的任何访问者交互。 如果需要引入与节点相关的某个行为, 你只需要实现一个新的访问者类即可。

真实世界类比

在这里插入图片描述
优秀的保险代理人总能为不同类型的团体提供不同的保单。

假如有这样一位非常希望赢得新客户的资深保险代理人。 他可以拜访街区中的每栋楼, 尝试向每个路人推销保险。 所以, 根据大楼内组织类型的不同, 他可以提供专门的保单:

如果建筑是居民楼, 他会推销医疗保险。
如果建筑是银行, 他会推销失窃保险。
如果建筑是咖啡厅, 他会推销火灾和洪水保险。

访问者模式结构

在这里插入图片描述
1、访问者 (Visitor) 接口声明了一系列以对象结构的具体元素为参数的访问者方法。 如果编程语言支持重载, 这些方法的名称可以是相同的, 但是其参数一定是不同的。

2、具体访问者 (Concrete Visitor) 会为不同的具体元素类实现相同行为的几个不同版本。

3、元素 (Element) 接口声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。

4、具体元素 (Concrete Element) 必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。 请注意, 即使元素基类实现了该方法, 所有子类都必须对其进行重写并调用访问者对象中的合适方法。

5、客户端 (Client) 通常会作为集合或其他复杂对象 (例如一个组合树) 的代表。 客户端通常不知晓所有的具体元素类, 因为它们会通过抽象接口与集合中的对象进行交互。

伪代码

在本例中, 访问者模式为几何图像层次结构添加了对于 XML 文件导出功能的支持。
在这里插入图片描述
通过访问者对象将各种类型的对象导出为 XML 格式文件。

// 元素接口声明了一个`accept(接收)`方法,它会将访问者基础接口作为一个参
// 数。
interface Shape ismethod move(x, y)method draw()method accept(v: Visitor)// 每个具体元素类都必须以特定方式实现`accept`方法,使其能调用相应元素类的
// 访问者方法。
class Dot implements Shape is// ……// 注意我们正在调用的`visitDot(访问点)`方法与当前类的名称相匹配。// 这样我们能让访问者知晓与其交互的元素类。method accept(v: Visitor) isv.visitDot(this)class Circle implements Shape is// ……method accept(v: Visitor) isv.visitCircle(this)class Rectangle implements Shape is// ……method accept(v: Visitor) isv.visitRectangle(this)class CompoundShape implements Shape is// ……method accept(v: Visitor) isv.visitCompoundShape(this)// 访问者接口声明了一组与元素类对应的访问方法。访问方法的签名能让访问者准
// 确辨别出与其交互的元素所属的类。
interface Visitor ismethod visitDot(d: Dot)method visitCircle(c: Circle)method visitRectangle(r: Rectangle)method visitCompoundShape(cs: CompoundShape)// 具体访问者实现了同一算法的多个版本,而且该算法能与所有具体类进行交互。
//
// 访问者模式在复杂对象结构(例如组合树)上使用时能发挥最大作用。在这种情
// 况下,它可以存储算法的一些中间状态,并同时在结构中的不同对象上执行访问
// 者方法。这可能会非常有帮助。
class XMLExportVisitor implements Visitor ismethod visitDot(d: Dot) is// 导出点(dot)的 ID 和中心坐标。method visitCircle(c: Circle) is// 导出圆(circle)的 ID 、中心坐标和半径。method visitRectangle(r: Rectangle) is// 导出长方形(rectangle)的 ID 、左上角坐标、宽和长。method visitCompoundShape(cs: CompoundShape) is// 导出图形(shape)的 ID 和其子项目的 ID 列表。// 客户端代码可在不知晓具体类的情况下在一组元素上运行访问者操作。“接收”操
// 作会将调用定位到访问者对象的相应操作上。
class Application isfield allShapes: array of Shapesmethod export() isexportVisitor = new XMLExportVisitor()foreach (shape in allShapes) doshape.accept(exportVisitor)

如果你并不十分理解为何本例中需要使用 accept接收方法, 我的一篇文章Visitor and Double Dispatch详细解释了这个问题。

访问者模式适合应用场景

1、如果你需要对一个复杂对象结构 (例如对象树) 中的所有元素执行某些操作, 可使用访问者模式。

访问者模式通过在访问者对象中为多个目标类提供相同操作的变体, 让你能在属于不同类的一组对象上执行同一操作。

2、可使用访问者模式来清理辅助行为的业务逻辑。

该模式会将所有非主要的行为抽取到一组访问者类中, 使得程序的主要类能更专注于主要的工作。

3、当某个行为仅在类层次结构中的一些类中有意义, 而在其他类中没有意义时, 可使用该模式。

你可将该行为抽取到单独的访问者类中, 只需实现接收相关类的对象作为参数的访问者方法并将其他方法留空即可。

实现方式

1、在访问者接口中声明一组 “访问” 方法, 分别对应程序中的每个具体元素类。

2、声明元素接口。 如果程序中已有元素类层次接口, 可在层次结构基类中添加抽象的 “接收” 方法。 该方法必须接受访问者对象作为参数。

3、在所有具体元素类中实现接收方法。 这些方法必须将调用重定向到当前元素对应的访问者对象中的访问者方法上。

4、元素类只能通过访问者接口与访问者进行交互。 不过访问者必须知晓所有的具体元素类, 因为这些类在访问者方法中都被作为参数类型引用。

5、为每个无法在元素层次结构中实现的行为创建一个具体访问者类并实现所有的访问者方法。

你可能会遇到访问者需要访问元素类的部分私有成员变量的情况。 在这种情况下, 你要么将这些变量或方法设为公有, 这将破坏元素的封装; 要么将访问者类嵌入到元素类中。 后一种方式只有在支持嵌套类的编程语言中才可能实现。

6、客户端必须创建访问者对象并通过 “接收” 方法将其传递给元素。

访问者模式优缺点

在这里插入图片描述

与其他模式的关系

你可以将访问者模式视为命令模式的加强版本, 其对象可对不同类的多种对象执行操作。

你可以使用访问者对整个组合模式树执行操作。

可以同时使用访问者迭代器模式来遍历复杂数据结构, 并对其中的元素执行所需操作, 即使这些元素所属的类完全不同。

C++ 访问者模式讲解和代码示例

访问者是一种行为设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。

使用示例: 访问者不是常用的设计模式, 因为它不仅复杂, 应用范围也比较狭窄。

概念示例

本例说明了访问者设计模式的结构并重点回答了下面的问题:

它由哪些类组成?
这些类扮演了哪些角色?
模式中的各个元素会以何种方式相互关联?

main.cc: 概念示例

/*** The Visitor Interface declares a set of visiting methods that correspond to* component classes. The signature of a visiting method allows the visitor to* identify the exact class of the component that it's dealing with.*/
class ConcreteComponentA;
class ConcreteComponentB;class Visitor {public:virtual void VisitConcreteComponentA(const ConcreteComponentA *element) const = 0;virtual void VisitConcreteComponentB(const ConcreteComponentB *element) const = 0;
};/*** The Component interface declares an `accept` method that should take the base* visitor interface as an argument.*/class Component {public:virtual ~Component() {}virtual void Accept(Visitor *visitor) const = 0;
};/*** Each Concrete Component must implement the `Accept` method in such a way that* it calls the visitor's method corresponding to the component's class.*/
class ConcreteComponentA : public Component {/*** Note that we're calling `visitConcreteComponentA`, which matches the* current class name. This way we let the visitor know the class of the* component it works with.*/public:void Accept(Visitor *visitor) const override {visitor->VisitConcreteComponentA(this);}/*** Concrete Components may have special methods that don't exist in their base* class or interface. The Visitor is still able to use these methods since* it's aware of the component's concrete class.*/std::string ExclusiveMethodOfConcreteComponentA() const {return "A";}
};class ConcreteComponentB : public Component {/*** Same here: visitConcreteComponentB => ConcreteComponentB*/public:void Accept(Visitor *visitor) const override {visitor->VisitConcreteComponentB(this);}std::string SpecialMethodOfConcreteComponentB() const {return "B";}
};/*** Concrete Visitors implement several versions of the same algorithm, which can* work with all concrete component classes.** You can experience the biggest benefit of the Visitor pattern when using it* with a complex object structure, such as a Composite tree. In this case, it* might be helpful to store some intermediate state of the algorithm while* executing visitor's methods over various objects of the structure.*/
class ConcreteVisitor1 : public Visitor {public:void VisitConcreteComponentA(const ConcreteComponentA *element) const override {std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n";}void VisitConcreteComponentB(const ConcreteComponentB *element) const override {std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n";}
};class ConcreteVisitor2 : public Visitor {public:void VisitConcreteComponentA(const ConcreteComponentA *element) const override {std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n";}void VisitConcreteComponentB(const ConcreteComponentB *element) const override {std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n";}
};
/*** The client code can run visitor operations over any set of elements without* figuring out their concrete classes. The accept operation directs a call to* the appropriate operation in the visitor object.*/
void ClientCode(std::array<const Component *, 2> components, Visitor *visitor) {// ...for (const Component *comp : components) {comp->Accept(visitor);}// ...
}int main() {std::array<const Component *, 2> components = {new ConcreteComponentA, new ConcreteComponentB};std::cout << "The client code works with all visitors via the base Visitor interface:\n";ConcreteVisitor1 *visitor1 = new ConcreteVisitor1;ClientCode(components, visitor1);std::cout << "\n";std::cout << "It allows the same client code to work with different types of visitors:\n";ConcreteVisitor2 *visitor2 = new ConcreteVisitor2;ClientCode(components, visitor2);for (const Component *comp : components) {delete comp;}delete visitor1;delete visitor2;return 0;
}

Output.txt: 执行结果

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2

来源:https://refactoringguru.cn/design-patterns/visitor
仅供学习,非商业用途,侵删

相关文章:

C++设计模式(19)——访问者模式

亦称&#xff1a; Visitor 意图 访问者模式是一种行为设计模式&#xff0c; 它能将算法与其所作用的对象隔离开来。 问题 假如你的团队开发了一款能够使用巨型图像中地理信息的应用程序。 图像中的每个节点既能代表复杂实体 &#xff08;例如一座城市&#xff09;&#xf…...

ChatGPT 的工作原理:机器人背后的模型

这篇对支持 ChatGPT 的机器学习模型的温和介绍&#xff0c;将从大型语言模型的介绍开始&#xff0c;深入探讨使 GPT-3 得到训练的革命性自我注意机制&#xff0c;然后深入研究人类反馈的强化学习&#xff0c;使 ChatGPT 与众不同的新技术。 大型语言模型 ChatGPT 是一类被称…...

FreeRTOS入门(04):中断、内存、追踪与调试

文章目录目的中断内存堆&#xff08;heap&#xff09;栈&#xff08;stack&#xff09;断言调试总结目的 有了前面的几篇文章 FreeRTOS 基本上已经可以在项目中使用上了&#xff1a; 《FreeRTOS入门&#xff08;01&#xff09;&#xff1a;基础说明与使用演示》 《FreeRTOS入门…...

【C语言】带你彻底理解指针(1)

✨✨✨✨如果文章对你有帮助记得点赞收藏关注哦&#xff01;&#xff01;✨✨✨✨ 文章目录指针的介绍&#xff1a;一、简单指针&#x1f308;1.1 指针的定义与使用1.2 指针与数组二、指针数组✨三、数组指针&#x1f31e;3.1 数组指针的定义3.2 ”数组名“与”&数组名“3.…...

C/C++ 中 JSON 库的使用 (CJSON/nlohmann)

C/C 中 JSON 库的使用 &#xff08;CJSON/nlohmann&#xff09;概述cjson基本操作从(字符指针)缓冲区中解析出JSON结构转成成JS字符串(将传入的JSON结构转化为字符串)将JSON结构所占用的数据空间释放JSON 值的创建创建一个值类型的数据创建一个对象&#xff08;文档&#xff09…...

【Opencv项目实战】目标检测:自动检测出现的所有动态目标

文章目录一、项目思路二、算法详解2.1、计算两个数组或数组与标量之间的每个元素的绝对差。2.2、轮廓检测 绘制物体轮廓 绘制矩阵轮廓2.3、连续窗口显示2.4、读取视频&#xff0c;显示视频&#xff0c;保存视频三、项目实战&#xff1a;实时动态目标检测实时动态目标检测一、…...

活动报名:Tapdata Cloud V3 最新功能全解与核心应用场景演示

作为中国的 “Fivetran/Airbyte”, Tapdata Cloud 自初版公测以来&#xff0c;已累积10,000 注册用户。核心场景包括 Any Source → Any Target 的实时数据库同步、数据入湖入仓&#xff0c;以及通用 ETL 处理等。近期&#xff0c;功能特性全面优化的 Tapdata Cloud V3 也已开放…...

人工智能AI威武,爱也……恨也……

人工智能AI威武&#xff0c;爱也&#xff01;恨也&#xff01;&#xff01;它会创作会代码&#xff0c;从它那儿能仿到更好的思维&#xff1b;多它那里可以学到更好的代码。它聪慧全能&#xff0c;成为一坨人偷懒神器&#xff1b;变成“智者”作弊的“倚天屠龙”&#xff01;&a…...

SpringBoot-基础篇

SpringBoot基础篇 ​ 在基础篇中&#xff0c;我给学习者的定位是先上手&#xff0c;能够使用SpringBoot搭建基于SpringBoot的web项目开发&#xff0c;所以内容设置较少&#xff0c;主要包含如下内容&#xff1a; SpringBoot快速入门SpringBoot基础配置基于SpringBoot整合SSMP…...

Tapdata Connector 实用指南:实时数仓场景之数据实时同步至 ClickHouse

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata 是一个以低延迟数据移动为核心优势构建的现代数据平台&#xff0c;内置 60 数据连接器&#xff0c;拥有稳定的实时采集和传输能力、秒级响应的数据实时计算能力、稳定易用的数据实时服务能力&#xff0c;以及低代码可视化操作…...

刷题专练之数组移除元素

文章目录前言一、移除元素1.题目介绍2.思路&#xff1a;3.代码二、移动零1.题目介绍2.思路3.代码三、删除有序数组中的重复项1.题目介绍2.思想3.代码四、80. 删除有序数组中的重复项 II1.题目介绍2.思路3.代码4.推荐题解前言 我每个刷题篇的题目顺序都是特别安排的&#xff0c;…...

常见激活函数Activation Function的选择

Activation Function激活函数一般会神经网络中隐层和输出层上&#xff0c;其中作用在输出层主要用于适配输出&#xff0c;比如sigmoid函数可用于生成[0,1]之间的概率估计值。而作用于隐层主要用于增加神经网络的非线性&#xff0c;增加了网络的表达能力&#xff0c;本文主要介绍…...

大厂跟进ChatGPT,为什么百度“文心一言”成色最好?【快评】

作者 | 曾响铃 文 | 响铃说 赶ChatGPT热度&#xff0c;百度3月初就要发布与ChatGPT类似的人工智能聊天机器人服务“文心一言”&#xff08;英文名&#xff1a;ERNIE Bot&#xff09;&#xff0c;似乎无法提振资本市场对百度的信心。 2022年第四季度及全年未经审计的财报发布…...

ChatGPT和Web3:人工智能如何帮助您建立和发展您的 Web3 公司

ChatGPT是OpenAI在2022年11月推出的聊天机器人。该机器人建立在OpenAI的GPT-3人工智能家族上&#xff0c;并通过监督学习和强化学习技术进行了优化。 与ChatGPT机器人聊天时&#xff0c;你会感觉自己在与一个懂得一切并以非常教育性的方式回答的朋友交谈。回答在许多知识领域非…...

【人工智能 AI】怎样实施RPA 机器人流程自动化(Robotic Process Automation)?核心技术有哪些?

文章目录 RPA 简介RPA的实施RPA的核心技术1. 自动化测试(1)自动化测试工具(2)自动化测试框架2. 自动化脚本(1)自动化脚本语言(2)自动化脚本框架3. 机器学习(1)机器学习模型(2)机器学习框架(3)自然语言处理(4)图像处理(5)深度学习(6)机器人操作系统RPA核心能…...

基于BP神经网络的性别识别,BP神经网络详细原理,自编码神经网络代码,神经网络案例之18

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 数据 神经网络参数 基于BP神经网络 性别识别的MATLAB代码 效果图 结果分析 展望 背影 男人体内蛋白质比例大&#xff0c;女生…...

2023年消费电子行业研究报告

第一章 行业概况 消费电子行业是电子信息行业的子行业。消费电子是指围绕着消费者应用而设计的与生活、工作和娱乐息息相关的电子类产品&#xff0c;通常会应用于娱乐、通讯以及文书用途&#xff0c;最终实现消费者自由选择资讯、享受娱乐的目的&#xff0c;主要侧重于个人购买…...

CSDN 编程竞赛三十一期题解

竞赛总览 CSDN 编程竞赛三十一期&#xff1a;比赛详情 (csdn.net) 本次竞赛的最后一道题的描述部分有些问题&#xff08;题目描述与样例不符&#xff09;&#xff0c;另外&#xff0c;测试数据似乎也有点问题&#xff0c;试了多种方式&#xff0c;但最多只能通过10%的测试点。…...

SpringMVC常见面试题(2023最新)

目录前言1.简单介绍下你对springMVC的理解?2.说一说SpringMVC的重要组件及其作用3.SpringMVC的工作原理或流程4.SpringMVC的优点5.SpringMVC常用注解6.SpringMVC和struts2的区别7.怎么实现SpringMVC拦截器8.SpringMvc的控制器是不是单例模式&#xff1f;如果是&#xff0c;有什…...

【正点原子FPGA连载】第十六章DP彩条显示实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十六章DP彩条显…...

数据结构与算法—链表list

目录 链表 链表类型 链表插入 链表删除 写程序注意点 与数组区别 链表应用 LRU 实现思想 链表 链表&#xff0c;一种提高数据读取性能的技术&#xff0c;在硬件设计、软件开发中有广泛应用。常见CPU缓存&#xff0c;数据库缓存&#xff0c;浏览器缓存等。缓存满时&#…...

自定义View练习题目整理

一、动态音频播放柱形图 1、效果图&#xff1a; 2、步骤 &#xff08;1&#xff09;、新建自定义View类&#xff0c;继承View &#xff08;2&#xff09;、重写onDraw()方法&#xff0c;使用画笔和画布循环画一定数量的柱形 Overrideprotected void onDraw(Canvas canvas) {s…...

LAMP平台部署及应用

LAMP平台部署及应用 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4c…...

ubuntu20.04安装python3虚拟环境

1.安装pip3 sudo apt install python3-pip2.安装虚拟环境 sudo apt install virtualenv sudo apt install virtualenvwrapper3.修改配置文件设置环境变量 打开.bashrc并编辑 gedit ~/.bashrc在.bashrc文件后面加入下面两行 export WORKON_HOME$HOME/.virtualenvs source …...

VUE3源码分析————rollup打包

文章目录什么是rolluprollup打包和webpack打包的区别rollup打包准备一、安装yarn开始rollup打包一、初始化二、package.json文件配置三、新建并配置打包文件夹四、下载rollup及打包执行文件五、文件大致分布![image.png](https://img-blog.csdnimg.cn/img_convert/66f1a85ff57d…...

【JavaScript】前端实现电子签名:

文章目录一、效果:二、实现:三、扩展一、效果: 二、实现: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…...

Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Feb 2023)

Windows 11, version 22H2&#xff0c;2023 年 2 月 更新 请访问原文链接&#xff1a;https://sysin.org/blog/windows-11/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org 全新推出 Windows 11 全新 Windows 体验&#x…...

【java】Spring Cloud --Spring Cloud Alibaba 教程

文章目录Spring Cloud Alibaba是什么Spring Cloud AlibabaSpring Cloud Alibaba 组件Spring Cloud Alibaba 的应用场景Spring Cloud 两代实现组件对比Spring Cloud Alibaba 版本依赖Spring Cloud Alibaba 组件版本关系Spring Cloud Alibaba NacosNacos 的特性服务发现服务健康监…...

通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作增加编程要求

2.编程要求&#xff1a; 1&#xff09;结构体封装 typedef struct{ char* cmd_arr; //命令行字符串 gpio_t* gpiox;//GPIO组号 unsigned int pin; //引脚编号 status_t status; //LED灯状态 void(*gpio_write_pin)(gpio_t* gpiox,unsigned int pin,status_t status); }cmd_t; 2…...

银行家算法

银行家算法 银行家算法是一种用来避免操作系统死锁出现的有效算法&#xff0c;所以在引入银行家算法的解释之前&#xff0c;有必要简单介绍一下死锁的概念。 一、死锁 死锁&#xff1a;是指两个或两个以上的进程在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成…...