C++设计模式行为模式———中介者模式
文章目录
- 一、引言
- 二、中介者模式
- 三、总结
一、引言
中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。
中介者模式可以减少对象之间混乱无序的依赖关系,从而使其耦合松散,限制对象之间的直接交互,迫使它们通过一个中介者对象进行合作。
二、中介者模式
典型的计算机是由CPU、内存、硬盘、声卡、显卡、网卡等配件构成,这些配件都插在主板上,主板对于这些计算机配件来讲,就是一个中介者,各个配件之间的数据通信和交互都通过主板进行。设想一下从硬盘上读一个图形文件并显示到屏幕上,涉及计算机配件可能包括硬盘、CPU、内存、显卡等,通过主板,这些配件可以协调工作。但是,如果没有主板,配件之间的数据交换就麻烦了。从理论上来说,每个计算机配件之间都存在着数据交换的可能,这种数据交换看起来非常杂乱,呈现一种网状结构,这种网状结构体现的是一种多对多的关系(一个配件可能要跟其他多个配件进行交互)。
但如果有了主板这个中介者的存在,每个配件只需要与主板打交道,主板可以把某个配件的信息发送给其他配件,配件之间不再需要打交道,那么此时数据的交换就呈现出了一种星状结构,这种结构看起来就简单和清晰。
假设我们打算开发一个游戏的登录功能,一种是“游客登录”,一种是“账号登录”。用户只能使用一种方式进行登录。
选择“游客登录”的好处是不用输入账号和密码就可以直接单击“登录”按钮来登录游戏,坏处是游戏进度只能保存在当前计算机中,如果换一台计算机,游戏进度信息将全部丢失。选择“账号登录”的麻烦之处是需要输人账号和密码后才能单击“登录”按钮来登录游戏,好处是即便换一台计算机,输人同样的账号和密码登录游戏后游戏的进度信息会全部保留。
这个登录界面需要多个UI控件,如单选按钮(登陆方式),编辑框(账号密码),普通按钮等。如果不采用中间模式来编辑代码,代码会是什么样子的呢?
首先先创建一个UI控件的父类:
class CtlParent{
public:CtlParent(string caption):m_caption(caption){}virtual ~CtlParent(){}//当UI控件发生变化时该成员函数会被调用virtual void Changed(map<string, CtlParent*>& tmpuictllist) = 0;//形参所代表的map容器中包含着所有对话框中涉及的UI控件,注意文件头要有#include<map>//设置UI控件启用或禁用virtual void Enable(bool sign) = 0;
protected:string m_caption;//控件上面显示的文字内容,本范例假设每个UI控件上的文字都不同
};
紧接着分别创建普通按钮、单选按钮、编辑控件等。有了Button
、RadioBtn
、EditCtl
子类的定义,接下来就可以实现这些子类的Changed
成员函数了。
class Button : public CtlParent {
public:Button(string caption) :CtlParent(caption) {}//设置按钮的启用或禁用virtual void Enable(bool sign) {if (sign == true)cout << "按钮" << m_caption << "被设置为了启用状态" << endl;elsecout << "按钮" << m_caption << "被设置为了 禁用 / 状态" << endl;//具体实现按钮启用或者禁用的代码略…}//按钮被按下时该成员函数会被调用virtual void Changed(map<string, CtlParent*>& tmpuictllist){if (m_caption == "登录")//按下的是登录按钮cout << "开始游戏登录验证,根据验证结果决定是进人游戏之中还是验证失败给出提示!"<< endl;else if (m_caption == "退出")//按下的是退出按钮,则退出整个游戏cout << "游戏退出,再见!" << endl;}
};//单选按钮相关类
class RadioBtn :public CtlParent {
public:RadioBtn(string caption) :CtlParent(caption) {}//构造函数
public://设置单选按钮的启用或禁用virtual void Enable(bool sign) {//本范例用不到该功能,实现代码略…//设置单选按钮为被选中或者被取消选中,被选中的单选按钮中间有个黑色实心圆点}void Selected(bool sign){if (sign == true)cout << "单选按钮" << m_caption << "被选中" << endl;elsecout << "单选按钮" << m_caption << "被取消选中" << endl;//具体实现单选按钮被选中或者被取消选中的代码略…}//单选按钮被单击时该成员函数会被调用virtual void Changed(map< string, CtlParent*>& tmpuictllist){if (m_caption == "游客登录"){(static_cast<RadioBtn*>(tmpuictllist["游客登录"]))->Selected(1);(static_cast<RadioBtn*>(tmpuictllist["账号登录"]))->Selected(0);tmpuictllist["账号"]->Enable(0);tmpuictllist["密码"]->Enable(0);tmpuictllist["登录"]->Enable(1);}else if (m_caption == "账号登陆"){(static_cast<RadioBtn*>(tmpuictllist["游客登录"]))->Selected(0);(static_cast<RadioBtn*>(tmpuictllist["账号登录"]))->Selected(1);tmpuictllist["账号"]->Enable(1);tmpuictllist["密码"]->Enable(1);tmpuictllist["登录"]->Enable(1);if ((static_cast<EditCtl*>(tmpuictllist["账号"]))->isContentEmpty() || (static_cast<EditCtl*>(tmpuictllist["密码"]))->isContentEmpty()){tmpuictllist["登录"]->Enable(0);}else{tmpuictllist["登录"]->Enable(1);}}}
};
//编辑框相关类
class EditCtl :public CtlParent
{
public:EditCtl(string caption) :CtlParent(caption) {}//构造函数//设置编辑框的启用或禁用void Enable(bool sign){if (sign == true)cout << "编辑框" << m_caption << "被设置为了\"启用\"状态" << endl;elsecout << "编辑框" << m_caption << "被设置为了\"禁用\"状态" << endl;//具体实现编辑框启用或者禁用的代码略…}//是否编辑框中的内容为空bool isContentEmpty() {return m_content.empty();}//编辑框内容发生变化时该成员函数会被调用virtual void Changed(map<string, CtlParent*>& tmpuictllist){if ((static_cast<EditCtl*>(tmpuictllist["账号"]))->isContentEmpty() || (static_cast<EditCtl*>(tmpuictllist["密码"]))->isContentEmpty()){tmpuictllist["登录"]->Enable(0);}else{tmpuictllist["登录"]->Enable(1);}}
private:string m_content = "";//编辑框中的内容
};
在main函数中调用:
// 创建UI控件列表
map<string, CtlParent*> uictllist;// 创建按钮
uictllist["登录"] = new Button("登录");
uictllist["退出"] = new Button("退出");// 创建单选按钮
uictllist["游客登录"] = new RadioBtn("游客登录");
uictllist["账号登录"] = new RadioBtn("账号登录");// 创建编辑框
uictllist["账号"] = new EditCtl("账号");
uictllist["密码"] = new EditCtl("密码");(dynamic_cast<RadioBtn*>(uictllist["游客登录"]))->Selected(true);
//"游客登录"单选按钮设置为选中
(dynamic_cast<RadioBtn*>(uictllist["账号登录"]))->Selected(false);
//"账号登录"单选按钮设置为取消选中
uictllist["账号"]->Enable(false);//"账号"编辑框设置为禁用
uictllist["密码"]->Enable(false);//"密码"编辑框设置为禁用
uictllist["登录"]->Enable(true);//"登录"按钮设置为启用
uictllist["退出"]->Enable(true);//"退出"按钮设置为启用
cout << "-----------------\n";
uictllist["账号登录"]->Changed(uictllist);
// 清理资源
for (auto& pair : uictllist) {delete pair.second;
}
可以看到这样是十分麻烦的。当“账号登录”单选按钮被选中时,会影响到许多其他控件的状态(调用了许多其他控件所属类的成员函数),能看到的是Button
、RadioBtn
、EditCtl
类的Changed
成员函数中还包含各种代码,这属于典型的网状结构一个对象(控件)发生改变时会与许多其他对象进行交互,对象之间彼此耦合,相互纠缠、制约和依赖,大大降低了对象的可复用性。如果新增或删除一个UI控件类,那么与该UI控件交互的其他UI控件类也必须进行修改,而这就违反了开闭原则。
此时就可以使用中介者模式。在中介者模式中会引人一个中介者对象,各个UI控件之间不再需要彼此通信,只与中介者对象进行通信,中介者会把某个UI控件发送来的信息传达给其他UI控件,这样,以往分散在各个UI控件子类中的逻辑处理代码(Button
、RadioBtn
、EditCtl
类的Changed
成员函数中的代码)就可以统一写在中介者类中,中介者负责控制和协调一组对象之间的交互,某个对象不需要知道其他对象的存在,只需要知道中介者的存在并与其打交道即可。
首先,按照中介者设计模式的编写惯例,会先创建一个中介者抽象父类Mediator
,其中会声明一个成员函数(ctlChanged
)接口,UI控件通过调用这个成员函数与中介者对象进行交互。同时,中介者对象为了能够与所有的UI控件交互,也会持有所有UI控件的指针(createCtrl
成员函数)。定义UI控件类的父类CtlParent
,为了与中介者类对象交互,该类中必须有一个指向中介者类对象的指针m_pmediator
,同时应注意其中的Changed
成员函数,控件就是通过该成员函数与中介者对象进行交互的,
Mediator
类和CtlParent
代码如下:
class CtlParent;//中介者父类
class Mediator
{
public:virtual ~Mediator() {}virtual void createCtrl() = 0;virtual void ctlChanged(CtlParent*) = 0;
};
class CtlParent
{
public:CtlParent(Mediator* ptmpm, string caption):m_pmediator(ptmpm), m_caption(caption) {}virtual ~CtlParent() {}//当UI控件发生变化时该成员函数会被调用virtual void Changed(){m_pmediator->ctlChanged(this);}virtual void Enable(bool sign) = 0; //设置UI控件启用或禁用
protected:string m_caption;//控件上面显示的文字内容,本范例假设每个UI控件上的文字都不同Mediator* m_pmediator;
};
接着,分别创建普通按钮、单选按钮、编辑控件相关的类,它们都继承自CtlParent
类,注意,每个类中构造函数的第一个参数都是用于指向中介者类对象的指针:
class Button : public CtlParent {
public:Button(Mediator* ptmpm, string caption):CtlParent(ptmpm, caption) {}//设置按钮的启用或禁用virtual void Enable(bool sign) {if (sign == true)cout << "按钮" << m_caption << "被设置为了启用状态" << endl;elsecout << "按钮" << m_caption << "被设置为了 禁用 / 状态" << endl;//具体实现按钮启用或者禁用的代码略…}};
//编辑框相关类
class EditCtl :public CtlParent
{
public:EditCtl(Mediator* ptmpm, string caption):CtlParent(ptmpm, caption) {}//设置编辑框的启用或禁用void Enable(bool sign){if (sign == true)cout << "编辑框" << m_caption << "被设置为了\"启用\"状态" << endl;elsecout << "编辑框" << m_caption << "被设置为了\"禁用\"状态" << endl;//具体实现编辑框启用或者禁用的代码略…}//是否编辑框中的内容为空bool isContentEmpty() {return m_content.empty();}
private:string m_content = "";//编辑框中的内容
};//单选按钮相关类
class RadioBtn :public CtlParent {
public:RadioBtn(Mediator* ptmpm, string caption):CtlParent(ptmpm, caption) {}public://设置单选按钮的启用或禁用virtual void Enable(bool sign) {//设置单选按钮为被选中或者被取消选中,被选中的单选按钮中间有个黑色实心圆点}void Selected(bool sign){if (sign == true)cout << "单选按钮" << m_caption << "被选中" << endl;elsecout << "单选按钮" << m_caption << "被取消选中" << endl;//具体实现单选按钮被选中或者被取消选中的代码略…}
};
有了上述这些UI控件类,按照中介者设计模式的编写惯例,就可以编写具体的中介者类concrMediator
,该类继承自中介者抽象父类Mediator
,代码如下:
class concrMediator :public Mediator
{
public:~concrMediator(){delete mp_login;delete mp_logout;delete mp_rbtn1;delete mp_rbtn2;delete mp_edtctl1;delete mp_edtctl2;}virtual void createCtrl(){mp_login = new Button(this, "登录");mp_logout = new Button(this, "退出");mp_rbtn1 = new RadioBtn(this, "游客登录");mp_rbtn2 = new RadioBtn(this, "账号登录");mp_edtctl1 = new EditCtl(this, "账号");mp_edtctl2 = new EditCtl(this, "密码");//设置一下默认的UI控件状态mp_rbtn1->Selected(true);//"游客登录"单选按钮设置为选中mp_rbtn2->Selected(false);//"账号登录"单选按钮设置为取消选中mp_edtctl1->Enable(false);//"账号"编辑框设置为禁用mp_edtctl2->Enable(false);//"密码"编辑框设置为禁用mp_login->Enable(true);//"登录"按钮设置为启用mp_logout->Enable(true);//"退出"按钮设置为启用}virtual void ctlChanged(CtlParent* p_ctrl){if (p_ctrl == mp_login) {cout << "开始游戏登录验证,根据验证结果决定是进人游戏之中还是验证失败给出提示! " << endl;}else if (p_ctrl == mp_logout){cout << "游戏退出,再见! " << endl;}else if (p_ctrl == mp_rbtn1){mp_rbtn1->Selected(true);mp_rbtn2->Selected(false);mp_edtctl1->Enable(false);mp_edtctl2->Enable(false);mp_login->Enable(true);}else if (p_ctrl == mp_rbtn2) {mp_rbtn1->Selected(false);mp_rbtn2->Selected(true);mp_edtctl1->Enable(true);mp_edtctl2->Enable(true);mp_login->Enable(!mp_edtctl1->isContentEmpty() && !mp_edtctl2->isContentEmpty());}else if (p_ctrl == mp_edtctl1 || p_ctrl == mp_edtctl2){if (mp_edtctl1->isContentEmpty() || mp_edtctl2->isContentEmpty()){mp_login->Enable(false);}else{mp_login->Enable(true);}}}Button* mp_login = nullptr; //登录按钮Button* mp_logout = nullptr; //退出按钮RadioBtn* mp_rbtn1 = nullptr; //游客登录单选按钮RadioBtn* mp_rbtn2 = nullptr; //账号登录单选按钮EditCtl* mp_edtctl1 = nullptr; //账号编辑框EditCtl* mp_edtctl2 = nullptr; //密码编辑框};
int main() {concrMediator mymedia;mymedia.createCtrl();cout << "当\"账号登录\"单选按钮被按下时:" << endl;mymedia.mp_rbtn2->Changed();//模拟"账号登录"单选按钮被按下,则去通知//中介者,由中介者实现具体的逻辑处理
}
这样实现起来就轻松许多,“账号登录”单选按钮被选中时,会通知中介者处理,所有核心的处理代码都在中介者类中,因为中介者类中有所有UI控件对象的指针,所以中介者可以通过这些指针调用这些UI控件对象的成员函数,以达到正确设置这些UI控件状态的目的,这就相当于通知其他UI控件某个UI控件发生了变动。
中介者模式结构
引人中介者模式的定义:用一个中介对象(中介者)来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互方式。
中介者模式一般包含四种角色:
- 抽象中介者(Mediator):定义一些接口(抽象方法),后面谈到的各个同事对象(各个UI控件)可以调用这些接口来与中介者进行通信。这里指
Mediator
类。 - 具体中介者(ConcreteMediator):继承自抽象中介者类,实现父类中定义的抽象方法,其中保存了各个同事对象的指针用于与同事对象进行通信实现协作行为。这里指
concrMediator
类 - 抽象同事类(Colleague):定义同事类共有的方法(例如
Changed
)和一些需要子类实现的抽象方法(例如Enable
),同时维持了一个指向中介者对象的指针(m_pmediator
),用于与中介者对象通信。这里指CtlParent
类。 - 具体同事类(ConcreteCollegue):继承自抽象同事类,实现父类中定义的抽象方法。以往同事类对象之间的通信在引人中介者模式后,具体的同事类对象就不再需要与其他同事类对象通信(不需要了解其他同事类对象),只需要与中介者通信,中介者会根据实际情况完成与其他同事类对象的通信。这里指
Button
、RadioBtn
、EditCtl
类。
中介者模式一般用于一组以定义良好但复杂的方式进行通信的场合,由终结者负责控制和协调一组对象的交互。
三、总结
中介者将对象之间复杂的沟通和控制方式集中起来处理,将以往对象之间复杂的多对多关系转化为简单的一对多关系。让系统有更好的灵活性和可扩展性。中介者模式将同事对象进行解耦,同事对象之间不再是一种紧耦合关系,它们仅知道中介者,从而减少相互连接的数目。每个同事类对象都可以独立地改变而不会影响到其他同事类对象,更符合开闭原则,进一步增加了对象的复用性。
而且将各种控制逻辑的代码(同事类对象之间的交互)都集中(转移)到了中介者类中实现,这为集中修改提供了便利,而且若将来需要引人新的中介者行为,则可以创建新具体中介者类来实现。代码的集中实现简化了项目的维护,同时类对象之间传递消息的通路变得简单,消除了对象之间错综复杂的关系,但这也会使中介者类的实现变得非常复杂甚至难以维护,尤其是当同事对象之间的交互非常多时,所以,要平衡好同事对象之间交互的复杂度和中介者对象的实现复杂度,再决定是否使用中介者模式,如果中介者的实现太复杂,可能会抵消掉使用该模式在其他方面带来的好处。
也就是说,当组件因过于依赖其他组件而无法在不同应用中复用时, 可使用中介者模式。
应用中介者模式后, 每个组件不再知晓其他组件的情况。 尽管这些组件无法直接交流, 但它们仍可通过中介者对象进行间接交流。 如果你希望在不同应用中复用一个组件, 则需要为其提供一个新的中介者类。
中介者方式实现方式:
-
找到一组当前紧密耦合, 且提供其独立性能带来更大好处的类 (例如更易于维护或更方便复用)。
-
声明中介者接口并描述中介者和各种组件之间所需的交流接口。 在绝大多数情况下, 一个接收组件通知的方法就足够了。
如果你希望在不同情景下复用组件类, 那么该接口将非常重要。 只要组件使用通用接口与其中介者合作, 你就能将该组件与不同实现中的中介者进行连接。
-
实现具体中介者类。 该类可从自行保存其下所有组件的引用中受益。
-
你可以更进一步, 让中介者负责组件对象的创建和销毁。 此后, 中介者可能会与工厂或外观类似。
-
组件必须保存对于中介者对象的引用。 该连接通常在组件的构造函数中建立, 该函数会将中介者对象作为参数传递。
-
修改组件代码, 使其可调用中介者的通知方法, 而非其他组件的方法。 然后将调用其他组件的代码抽取到中介者类中, 并在中介者接收到该组件通知时执行这些代码。
中介者模式实现了单一职责原则和开闭原则。可以将多个组件间的交流抽取到同一位置, 使其更易于理解和维护,而且无需修改实际组件就能增加新的中介者。减轻了多个组件之间的耦合。
外观模式和中介者的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
- 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
tract-factory)或外观类似。
-
组件必须保存对于中介者对象的引用。 该连接通常在组件的构造函数中建立, 该函数会将中介者对象作为参数传递。
-
修改组件代码, 使其可调用中介者的通知方法, 而非其他组件的方法。 然后将调用其他组件的代码抽取到中介者类中, 并在中介者接收到该组件通知时执行这些代码。
中介者模式实现了单一职责原则和开闭原则。可以将多个组件间的交流抽取到同一位置, 使其更易于理解和维护,而且无需修改实际组件就能增加新的中介者。减轻了多个组件之间的耦合。
外观模式和中介者的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
- 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
- 中介者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。
相关文章:
C++设计模式行为模式———中介者模式
文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...
Perfetto学习大全
Perfetto 是一个功能强大的性能分析和追踪工具,主要用于捕获和分析复杂系统中的事件和性能数据,特别是在 Android 和 Linux 环境下。它的核心目标是帮助开发者深入了解系统和应用程序的运行状态,以便优化性能和诊断问题。 Perfetto的主要作用…...
管家婆财贸ERP BR040.销售单明细表变更
最低适用版本: C系列 23.8 插件简要功能说明: 销售明细表支持直接修改单据自由项1-16更多细节描述见下方详细文档 插件操作视频: 进销存类定制插件--销售单明细表变更 1. 应用中心增加菜单【销售单明细表变更】 a. 复制23.8版本销售单明细…...
2411rust,实现特征
原文 在Rust2024中,impl Trait在中位置的默认工作方式有了变化.是为了简化impl Trait,以更好地匹配人们一般的需求. 还添加了一个灵活的语法,让你需要时可完全控制. 从Rust2024开始,一直在更改,何时可在返回位置impl Trait的隐藏类型中使用泛型参数的规则: 1,即对返回位置i…...
SpringBoot3与JUnit5集成测试
你可以在 Spring Boot 3 中轻松设置和运行 JUnit 集成测试。合理使用 Spring 提供的注解和工具,可以确保测试的高效性和可靠性。以下是集成测试的步骤和示例: 1. 添加依赖 在 pom.xml 中添加 Spring Boot Starter Test 依赖,它包含 JUnit 5 …...
工程企业需要什么样的物资管理系统?为什么需要物资管理系统?
一、背景与意义 在工程项目的建设中,无论是高楼大厦的拔地而起,还是高速公路的绵延铺展,物资都是最基础的要素之一。从钢筋水泥到施工机械,任何一种物资的管理不善都可能导致项目延误、成本超支,甚至质量问题。然而&a…...
Vue + Websocket播放PCM(base64转ArrayBuffer、 字符串转ArrayBuffer)
文章目录 引言I 音视频处理相关概念和APIII 案例:基于开源库 pcm-player方式播放借助MediaSource和Audio对象播放音频流。基于原生api AudioContext 播放操作III 格式转换js字符串转ArrayBufferbase64 转 ArrayBufferIV 解决pcm-player分片播放问题引言 需求: 基于webscoket传…...
华为防火墙技术基本概念学习笔记
1.防火墙概述 1.1防火墙与交换机、路由器对比 路由器与交换机的本质是转发,防火墙的本质是控制。 防火墙与路由器、交换机是有区别的。路由器用来连接不同的网络,通过路由协议保证互联互通,确保将报文转发到目的地;交换机则通常用来组建局域…...
Mesh路由组网
Mesh无线网格网络,多跳(multi-hop)网络,为解决全屋覆盖信号,一般用于家庭网络和小型企业 原理 网关路由器(主路由,连接光猫),Mesh路由器(子路由,…...
【数据结构】七种常用排序总结
一、七种排序及其讲解 以下为七种排序的讲解: 【数据结构】插入排序——直接插入排序 和 希尔排序 【数据结构】选择排序——选择排序 和 堆排序 【数据结构】交换排序——冒泡排序 和 快速排序 【数据结构】归并排序 —— 递归及非递归解决归并排序 二、排序的…...
【在Linux世界中追寻伟大的One Piece】多线程(一)
目录 1 -> Linux线程概念 1.1 -> 什么是线程 1.2 -> 线程的优点 1.3 -> 线程的缺点 1.4 -> 线程异常 1.5 -> 线程用途 2 -> Linux线程 VS 进程 2.1 -> 线程和进程 2.2 -> 进程的多个线程共享 3 -> Linux线程控制 3.1 -> POSIX线程…...
《Python编程实训快速上手》第十天--处理CSV文件和JSON数据
CSV:简化的电子表格,被保存为纯文本文件 JSON:是一种数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,以JavaScript源代码的形式将信息保存在纯文本文件中 一、csv模块 CSV文件中的每行代表电…...
基于springboot停车场管理系统源码和论文
如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统停车场管理系统信息管理难度大,容错率低,…...
Linux的桌面
Linux的桌面是可以卸载的 的确,Linux并不像Windows,Linux本身是一个基于命令行的操作系统,在内核眼中,桌面只不过是个普通的应用程序,所以,在Linux的桌面中可以完成的事情,命令行中也基本可以完…...
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE 历史背景 javax变成Jakarta的主要原因是因为Java EE项目从Oracle转移到了Eclipse Foundation,并改名为Jakarta EE。 JavaEE是从Java 1.2版本开始推出的Java企业级开发平台,最初的名称是J2EE(J…...
java-排序算法汇总
排序算法: 冒泡排序(Bubble Sort) 选择排序(Selection Sort) 插入排序(Insertion Sort) 快速排序(Quick Sort) 归并排序(Merge Sort) 堆排序&…...
C0030.Clion中运行提示Process finished with exit code -1073741515 (0xC0000135)解决办法
1.错误提示 2.解决办法 添加环境变量完成之后,重启Clion软件,然后就可以正常调用由mingw编译的opencv库了。...
如何理解JS的 异步
JS是一门单线程的语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。 而渲染主线程承担着诸多的工作,渲染页面、执行JS都在其中运行。 如果使用同步的方式,就极有可能导致主线程产生阻塞,从而导致消…...
vsgithub
VS&GitHub项目联动(上传和克隆),创建你的第一个仓库,小白配置_vs上传代码到github-CSDN博客...
Android蓝牙架构,源文件目录/编译方式学习
Android 版本 发布时间 代号(Codename) Android 1.0 2008年9月23日 无 Android 1.1 2009年2月9日 Petit Four Android 1.5 2009年4月27日 Cupcake Android 1.6 2009年9月15日 Donut Android 2.0 2009年10月26日 Eclair Android 2.1 2…...
10 - Clickhouse集群部署以及副本和分片
目 一、副本 1、简介 2、副本写入流程 3、副本配置步骤 3.1、启动zookeeper集群 3.2、在 hallo100 的/etc/clickhouse-server/config.d 目录下创建一个名为metrika.xml 的配置文件,内容如下: 3.3、在 hallo100 的/etc/clickhouse-server/config.xml 中增加如…...
FreeRTOS的软件定时器与事件标志组
目录 1.FreeRTOS软件定时器 1.1 什么是FreeRTOS软件定时器? 1.2 学习软件定时器的意义 1.3 软件定时器的简介 1.3.1 软件定时器概述 1.3.2 编写回调函数的注意事项 1.4 定时器服务/Daemon 任务 1.4.1 定时器服务任务与队列 1.4.2 定时器相关配置 configUSE_T…...
生产制造领域的多元化模式探索
在当今全球化和信息化的时代背景下,生产制造领域正经历着前所未有的变革。随着消费者需求的多样化、市场竞争的加剧以及技术的不断进步,传统的生产制造模式已经难以满足现代企业的需求。因此,多种生产制造模式应运而生,以适应不同…...
大数据技术之SparkCore
RDD概述 什么是RDD RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。 RDD五大特性 RDD编程 RDD的创…...
element-plus的组件数据配置化封装 - table
目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…...
蓝桥杯每日真题 - 第15天
题目:(钟表) 题目描述(13届 C&C B组B题) 解题思路: 理解钟表指针的运动: 秒针每分钟转一圈,即每秒转6度。 分针每小时转一圈,即每分钟转6度。 时针每12小时转一圈…...
c#:winform调用bartender实现打印(学习整理笔记)
效果 学习路径 C# winform调用Bartender进行自定义打印、批量打印、检索文件夹中的模板_哔哩哔哩_bilibili 一、初始环境搭建见: c#:winform引入bartender-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/143989473?sharetypeblogdetail&s…...
周期法频率计的设计
目录 周期法频率计 分析: 设计过程: 周期法频率计 对于低频信号,应用周期法进行测频。周期法测频的基本原理是:应用标准频率信号统计被测信号两个相邻脉冲之间的脉冲数,然后通过脉冲数计算出被测信号的周期ÿ…...
【2024亚太杯亚太赛APMCM C题】数学建模竞赛|宠物行业及相关产业的发展分析与策略|建模过程+完整代码论文全解全析
第一个问题是:请基于附件 1 中的数据以及你的团队收集的额外数据,分析过去五年中国宠物行业按宠物类型的发展情况。并分析中国宠物行业发展的因素,预测未来三年中国宠物行业的发展。 第一个问题:分析中国宠物行业按宠物类型的发展…...
uniapp的renderjs使用
uniapp中的RenderJS主要服务于APP和H5平台,其作用包括降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力,以及在视图层操作DOM和运行Web的JS库。 RenderJS是uni-app中一个特性,它允许开发者在页面中使用JavaScript直接渲…...
CPU命名那些事
一、Intel CPU命名 1. 命名结构 Intel CPU 的命名通常包含以下几个部分: 品牌 产品线 系列 代数 具体型号 后缀 例如:Intel Core i7-13700K 2. 各部分含义 品牌 Intel:表示厂商(几乎所有命名中都有)。不同品…...
【LLM】一文学会SPPO
博客昵称:沈小农学编程 作者简介:一名在读硕士,定期更新相关算法面试题,欢迎关注小弟! PS:哈喽!各位CSDN的uu们,我是你的小弟沈小农,希望我的文章能帮助到你。欢迎大家在…...
鸿蒙多线程开发——线程间数据通信对象03(sendable)
1、简 介 在传统JS引擎上,对象的并发通信开销的优化方式只有一种,就是把实现下沉到Native侧,通过Transferable对象的转移或共享方式降低并发通信开销。而开发者仍然还有大量对象并发通信的诉求,这个问题在业界的JS引擎实现上并没…...
web前端开发--动画效果
1、3D旋转 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>3D旋转</title><style type"text/css">div{/*设置大盒子样式*/width: 100px;height: 100px;/*background-color: rgba(255,0,0,0.5);*/ba…...
【数据分享】中国价格统计年鉴(2013-2024) PDF
数据介绍 犹如一座珍贵的宝库,全面而系统地记录了中国在这一时期的价格变动情况。它涵盖了丰富的内容,包括宏观经济指标、商品价格、居民消费价格以及城市物价监测等多个方面。 在宏观经济指标方面,年鉴中收录了 GDP、CPI、PPI 等重要数据&…...
鸿蒙NEXT开发案例:字数统计
【引言】 本文将通过一个具体的案例——“字数统计”组件,来探讨如何在鸿蒙NEXT框架下实现这一功能。此组件不仅能够统计用户输入文本中的汉字、中文标点、数字、以及英文字符的数量,还具有良好的用户界面设计,使用户能够直观地了解输入文本…...
TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型
TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型 一、TritonServer中加载模型1.1 搭建本地仓库1.2 配置文件1.3 服务端代码1.4 启动TritonServer二、Gunicorn上启动Web服务2.1 安装和配置Gunicorn2.2 启动Gunicorn三、调用模型四、性能优化与监控五、总结在深度学习…...
[UE5学习] 一、使用源代码安装UE5.4
一、简介 本文介绍了如何使用源代码安装编译UE5.4,并且新建简单的项目,打包成安卓平台下的apk安装包。 二、使用源代码安装UE5.4 注意事项: 请保证可以全程流畅地科学上网。请保证C盘具有充足的空间。请保证接下来安装下载的visual studi…...
2023AE软件、Adobe After Effects安装步骤分享教程
2023AE软件是一款由Adobe公司开发的视频编辑软件,也被称为Adobe After Effects。它在广告、电影、电视和网络视频等领域广泛应用,用于制作动态图形、特效、合成和其他视觉效果。该软件支持多种视频和音频文件格式,具有丰富的插件和预设&#…...
Xilinx 7 系列 FPGA的各引脚外围电路接法
Xilinx 7系列FPGA的外围电路接法涉及到多个方面,包括电源引脚、时钟输入引脚、FPGA配置引脚、JTAG调试引脚,以及其他辅助引脚。 本文参考资料: ds180 - 7 Series FPGAs Data Sheet - Overview ds181 - Artix 7 FPGAs Data Sheet - DC and AC…...
【LeetCode热题100】队列+宽搜
这篇博客是关于队列宽搜的几道题,主要包括N叉树的层序遍历、二叉树的锯齿形层序遍历、二叉树最大宽度、在每个数行中找最大值。 class Solution { public:vector<vector<int>> levelOrder(Node* root) {vector<vector<int>> ret;if(!root) …...
<Sqlite><websocket>使用Sqlite与websocket,实现网页端对数据库的【读写增删】操作
前言 本文是在websocket进行通讯的基础,添加数据库进行数据的存储,数据库软件使用的是sqlite。 环境配置 系统:windows 平台:visual studio code 语言:javascript、html 库:nodejs、sqlite 概述 此前,我们实现在利用websocket和socket,将网页端与下位控制器如PLC进行…...
summernote富文本批量上传音频,视频等附件
普通项目,HTML的summernote富文本批量上传音频,视频等附件(其他附件同理) JS和CSS的引入 <head><th:block th:include"include :: summernote-css" /> </head> <body><th:block th:include"include :: summernote-js" /> …...
第六十五周周报 UP2ME
文章目录 week 65 UP2ME摘要Abstract1. 题目2. Abstract3. 文献解读3.1 Introduction3.2 创新点 4. 网络结构4.1 单变量预训练4.1.1 样例生成4.1.2 掩码自动编码器预训练4.1.3 即时反应模式 4.2 多元微调4.2.1 稀疏依赖图构造4.2.2 时域频道层 5. 实验结果6. 结论7. 部分关键代…...
Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容)
Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 目录 Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容) 一、简单介绍 二、实现原理 三、注意事项 四、案例简单步…...
【STM32】MPU6050简介
文章目录 MPU6050简介MPU6050关键块带有16位ADC和信号调理的三轴MEMS陀螺仪具有16位ADC和信号调理的三轴MEMS加速度计I2C串行通信接口 MPU6050对应的数据手册:MPU6050 陀螺仪加速度计 链接: https://pan.baidu.com/s/13nwEhGvsfxx0euR2hMHsyw?pwdv2i6 提取码: v2i6…...
学习日记_20241123_聚类方法(MeanShift)
前言 提醒: 文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。 其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展…...
Qt常用控件 按钮
文章目录 1. QAbstractButton 简介2. QPushButton2.1 例子1,设置按钮的图标2.2 例子2,设置按钮快捷键 3. QRadioButton3.1 介绍3.2 例子1,选择性别3.3 例子2,试试其他的信号3.3 例子3,分组 4. QCheckBox4.1 介绍4.2 例…...
医院信息化与智能化系统(22)
医院信息化与智能化系统(22) 这里只描述对应过程,和可能遇到的问题及解决办法以及对应的参考链接,并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图,可以试试PlantUML,告诉GPT你的文件结构,让他给你对应…...
嵌入式硬件实战基础篇(二)-稳定输出3.3V的太阳能电池-无限充放电
引言:本内容主要用作于学习巩固嵌入式硬件内容知识,用于想提升下述能力,针对学习稳压芯片和电容以及电池之间的运用,对于硬件PCB以及原理图的练习和前面硬件篇的实际运用;太阳能是一种清洁、可再生的能源,广…...