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

C++设计模式-结构型设计模式

写少量的代码来应对未来需求的变化。

单例模式

定义

保证一个类仅有一个实例,并提供一个该实例的全局访问点。——《设计模式》GoF

解决问题

稳定点:

  • 类只有一个实例,提供全局的访问点(抽象)

变化点:
有多个类都是单例,能不能复用代码(扩展中的继承和组合)

代码结构

  • 私有的构造和析构
  • 禁掉拷贝构造、拷贝赋值、移动构造、移动赋值
  • 静态类成员函数
  • 静态私有成员变量
  • 访问方式: Singleton:Getlnstance()

版本一

把构造函数和析构函数私有化,让别人不能调用它
因为只有一个示例,所以我们要限定他的构造函数
并且提供一个全局访问点

class Singleton {
public:static Singleton * GetInstance() {//全局访问点if (_instance == nullptr) {_instance = new Singleton();}return _instance;}
private:Singleton(){}; //构造~Singleton(){};Singleton(const Singleton &) = delete; //拷⻉
构造Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造static Singleton * _instance;
};Singleton* Singleton::_instance = nullptr;//静态成
员需要初始化

存在问题_instance是静态成员,它没有释放,没有delete这个,然后我们看版本二

版本二

针对上面问题版本二可以主动调用 atexit(Destructor);

class Singleton {
public:static Singleton * GetInstance() {if (_instance == nullptr) {_instance = new Singleton();//这里多线程的话会产生多个atexit(Destructor);}return _instance;}
private:static void Destructor() {if (nullptr != _instance) { //delete _instance;_instance = nullptr;}}Singleton(){}; //构造~Singleton(){};Singleton(const Singleton &) = delete; //拷⻉
构造Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造static Singleton * _instance;
};
Singleton* Singleton::_instance = nullptr;//静态成
员需要初始化
// 还可以使⽤ 内部类,智能指针来解决; 此时还有线程安全问题

存在问题:版本二不能够多线程的。

版本三

然后就用到了加锁,对于这个3.1和3.2很显然3.2这样效率更高,因为读的时候不需要加锁,只有写的时候才需要加锁。

#include <mutex>
class Singleton { // 懒汉模式 lazy load
public:static Singleton * GetInstance() {// std::lock_guard<std::mutex>
lock(_mutex); // 3.1 切换线程if (_instance == nullptr) {//双重检测std::lock_guard<std::mutex>
lock(_mutex); // 3.2if (_instance == nullptr) {//双重检测_instance = new Singleton();// 1. 分配内存// 2. 调用构造函数// 3. 返回指针// 多线程环境下 cpu reorder操作atexit(Destructor);}}return _instance;}
private:static void Destructor() {if (nullptr != _instance) {delete _instance;_instance = nullptr;}}Singleton(){}; //构造~Singleton(){};Singleton(const Singleton &) = delete; //拷⻉
构造Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造static Singleton * _instance;static std::mutex _mutex;
};
Singleton* Singleton::_instance = nullptr;//静态成
员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化

这里涉及到一个双重检测的问题:因为读的时候不需要加锁,只有写的时候才需要加锁,然后这里很巧妙,如果同时都进去第一个if语句的话,一开始只有一个能拿到锁,但是那个锁释放了,同时都进去第一层训话的那写也可以去申请,所以还要加上一个if,这就是双重检测

if (_instance == nullptr) {//双重检测std::lock_guard<std::mutex>
lock(_mutex); // 3.2if (_instance == nullptr) {//双重检测//操作,}
}

但是还是存在问题:
多核时代:它有一个编译器重排和cpu重排,然后让他以更快的速度执行,可能会违反顺序一致性。
然后C++为了解决这个问题,C++用同步原语,其中包括原子变量还要内存栅栏。
在这里插入图片描述
对于上面的代码,我们虽然加了一把锁,然后再new,但是我们没有考虑指令重排这个问题。因为这个new还要很多操作。1. 分配内存2. 调用构造函数3. 返回指针。再多个时代下,cpu可能会重排,因为再单线程的时代下,可能调用的是1,2,3.但是多线程可能调用的就是1,3,2。在其调用1,3之后就return了,然后下一个线程来了之后执行if是空的,然后它有调用了一次。

版本四

我们用原子语义,也就是无锁编程。
原子执行的问题:

  • 可见性问题
    • load 可以看到其他线程最新操作的数据
    • store 修改数据让其他线程可见
  • 执行序问题
    • 内存模型(memory_order_qcquire和memory_order_release)

内存阑珊

  • 可见性问题
  • 执行序问题
  Singleton* tmp =
_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acqui
re);//获取内存屏障if (tmp == nullptr) {std::lock_guard<std::mutex>
lock(_mutex);tmp =
_instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton;std::atomic_thread_fence(std::memory_order_relea
se);//释放内存屏障_instance.store(tmp,
std::memory_order_relaxed);atexit(Destructor);}}

我们用原子变量解决原则性和可见性问题
内存栅栏解决执行序问题

// volitile
#include <mutex>
#include <atomic>
class Singleton {
public:static Singleton * GetInstance() {Singleton* tmp =
_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acqui
re);//获取内存屏障if (tmp == nullptr) {std::lock_guard<std::mutex>
lock(_mutex);tmp =
_instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton;std::atomic_thread_fence(std::memory_order_relea
se);//释放内存屏障_instance.store(tmp,
std::memory_order_relaxed);atexit(Destructor);}}return tmp;}
private:static void Destructor() {Singleton* tmp =
_instance.load(std::memory_order_relaxed);if (nullptr != tmp) {delete tmp;}}Singleton(){}; //构造~Singleton(){};Singleton(const Singleton &) = delete; //拷⻉
构造Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造static std::atomic<Singleton*> _instance;//原子变量static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance;//静
态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
// g++ Singleton.cpp -o singleton -std=c++11

多线程下无锁编程的单例模式就解决了。但是代码太长了。

版本五

我们直接构造一个static类型。
c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。static是线程安全的,它再执行到下面之前都不会进行指令重排

// c++ effective
class Singleton
{
public:static Singleton& GetInstance() {static Singleton instance;return instance;}
private:Singleton(){}; //构造~Singleton(){};Singleton(const Singleton &) = delete; //拷⻉
构造Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造
};
// 继承 Singleton
// g++ Singleton.cpp -o singleton -std=c++11
/*该版本具备 版本5 所有优点:
1. 利⽤静态局部变量特性,延迟加载;
2. 利⽤静态局部变量特性,系统⾃动回收内存,⾃动调⽤析构函数;
3. 静态局部变量初始化时,没有 new 操作带来的cpu指令
reorder操作;
4. c++11 静态局部变量初始化时,具备线程安全;
*/

版本六

这个就是实现一个多态,因为如果有多个单例,我们就不用都写那些重复的代码了。

template<typename T>
class Singleton {
public:static T& GetInstance() {static T instance; // 这⾥要初始化
DesignPattern,需要调⽤DesignPattern 构造函数,同时会
调⽤⽗类的构造函数。return instance;}
protected://让子类得以构造virtual ~Singleton() {}Singleton() {} // protected修饰构造函数,才能让
别⼈继承
private:Singleton(const Singleton &) = delete; //拷⻉
构造禁用Singleton& operator=(const Singleton&) =
delete;//拷贝赋值构造Singleton(Singleton &&) = delete;//移动构造Singleton& operator=(Singleton &&) =
delete;//移动拷贝构造
};
class DesignPattern : public
Singleton<DesignPattern> {friend class Singleton<DesignPattern>; //
friend 能让Singleton<T> 访问到 DesignPattern构造函数
private:DesignPattern() {}~DesignPattern() {}
};

结构图

在这里插入图片描述

工厂模式

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。 ——《设计模式》GoF
为什么要有工厂模式,而不直接使用new?
除了new,还有复杂构造流程

解决问题

稳定性:

  • 创建同类对象的接口 对象创造接口
  • 同类对象有一个相同的职责 功能接口

变化点:

  • 创建对象的扩展

代码结构

实现该功能:实现一个导出数据的接口,让客户选择数据的导出方式(xml,Json,txt,csv);

没有使用工厂模式:

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:virtual bool Export(const std::string &data) = 0;virtual ~IExport(){}
};class ExportXml : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportJson : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};
// csv
class ExportTxt : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};// class ExportCSV : public IExport {
// public:
//     virtual bool Export(const std::string &data) {
//         return true;
//     }
// };// =====1
int main() {std::string choose/* = */;if (choose == "txt") {/***/IExport *e = new ExportTxt();/***/e->Export("hello world");} else if (choose == "json") {/***/IExport *e = new ExportJson();/***/e->Export("hello world");} else if (choose == "xml") {IExport *e = new ExportXml();e->Export("hello world");} else if (choose == "csv") {IExport *e = new ExportXml();e->Export("hello world");}
}

使用了工厂模式

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:virtual bool Export(const std::string &data) = 0;virtual ~IExport(){}
};class ExportXml : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportJson : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportTxt : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportCSV : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class IExportFactory {
public:IExportFactory() {_export = nullptr;}virtual ~IExportFactory() {if (_export) {delete _export;_export = nullptr;}}bool Export(const std::string &data) {if (_export == nullptr) {_export = NewExport();}return _export->Export(data);}
protected:virtual IExport * NewExport(/* ... */) = 0;
private:IExport* _export;
};class ExportXmlFactory : public IExportFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportXml();// 可能之后有什么操作return temp;}
};
class ExportJsonFactory : public IExportFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportJson;// 可能之后有什么操作return temp;}
};
class ExportTxtFactory : public IExportFactory {
protected:IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportTxt;// 可能之后有什么操作return temp;}
};class ExportCSVFactory : public IExportFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportCSV;// 可能之后有什么操作return temp;}
};int main () {IExportFactory *factory = new ExportCSVFactory();factory->Export("hello world");return 0;
}

对象创建接口

  • 创建具体对象
  • 调用功能接口

一个功能接口

设计原则
最小知道原则
面向接口编程

如何扩展代码

实现对象创建接口
实现功能接口
多态调用

总结

要点
解决创建过程比较复杂,希望对外隐藏这些细节的场景;

  • 比如连接池、线程池
  • 隐藏对象真实类型;
  • 对象创建会有很多参数来决定如何创建;
  • 创建对象有复杂的依赖关系;
    本质
    延迟到子类来选择实现;
    结构图
    在这里插入图片描述

抽象工厂模式

定义

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。 ——《设计模式》GoF

解决问题

稳定性:

  • 创建同类对象的接口 对象创造接口
  • 同类对象有多个相同的职责 功能接口

变化点:

  • 创建对象的扩展

代码结构

实现一个拥有导出导入数据的接口,让客户选择数据的导出导入方式
对象创建接口

  • 创建具体对象
  • 提供多个功能结构来调用

多个功能接口

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:virtual bool Export(const std::string &data) = 0;virtual ~IExport(){}
};class ExportXml : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportJson : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportTxt : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class ExportCSV : public IExport {
public:virtual bool Export(const std::string &data) {return true;}
};class IImport {
public:virtual bool Import(const std::string &data) = 0;virtual ~IImport(){}
};class ImportXml : public IImport {
public:virtual bool Import(const std::string &data) {return true;}
};class ImportJson : public IImport {
public:virtual bool Import(const std::string &data) {return true;}
};class ImportTxt : public IImport {
public:virtual bool Import(const std::string &data) {return true;}
};class ImportCSV : public IImport {
public:virtual bool Import(const std::string &data) {// ....return true;}
};class IDataApiFactory {
public:IDataApiFactory() {_export = nullptr;_import = nullptr;}virtual ~IDataApiFactory() {if (_export) {delete _export;_export = nullptr;}if (_import) {delete _import;_import = nullptr;}}bool Export(const std::string &data) {if (_export == nullptr) {_export = NewExport();}return _export->Export(data);}bool Import(const std::string &data) {if (_import == nullptr) {_import = NewImport();}return _import->Import(data);}
protected:virtual IExport * NewExport(/* ... */) = 0;virtual IImport * NewImport(/* ... */) = 0;
private:IExport *_export;IImport *_import;
};class XmlApiFactory : public IDataApiFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportXml;// 可能之后有什么操作return temp;}virtual IImport * NewImport(/* ... */) {// 可能有其它操作,或者许多参数IImport * temp = new ImportXml;// 可能之后有什么操作return temp;}
};class JsonApiFactory : public IDataApiFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportJson;// 可能之后有什么操作return temp;}virtual IImport * NewImport(/* ... */) {// 可能有其它操作,或者许多参数IImport * temp = new ImportJson;// 可能之后有什么操作return temp;}
};
class TxtApiFactory : public IDataApiFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportTxt;// 可能之后有什么操作return temp;}virtual IImport * NewImport(/* ... */) {// 可能有其它操作,或者许多参数IImport * temp = new ImportTxt;// 可能之后有什么操作return temp;}
};class CSVApiFactory : public IDataApiFactory {
protected:virtual IExport * NewExport(/* ... */) {// 可能有其它操作,或者许多参数IExport * temp = new ExportCSV;// 可能之后有什么操作return temp;}virtual IImport * NewImport(/* ... */) {// 可能有其它操作,或者许多参数IImport * temp = new ImportCSV;// 可能之后有什么操作return temp;}
};// 相关性  依赖性    工作当中
int main () {IDataApiFactory *factory = new CSVApiFactory();factory->Import("hello world");factory->Export("hello world");return 0;
}

工厂模式和抽象工厂模式的区别

  • 抽象工厂需要创建一系列功能对象(多个功能接口)
  • 工厂方法创建一类功能的对象

结构图

在这里插入图片描述

责任链

其中negix就是用到了责任链

定义

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 ——《设计模式》GoF
在这里插入图片描述
有多个处理者,并且连成一个链,当有一个处理了之后,后面的就不用处理了。

解决问题

稳定点:
处理流程

  • 请求按照链条传递
    • 链表关系
    • 接口
  • 可打断

变化点

  • 处理节点的个数
  • 处理顺序
  • 处理条件

代码框架

请求流程,1 天内需要主程序批准,3 天内需要项目经理批准,3 天以上需要老板批准;
首先不使用责任链模式:

#include <string>class Context {
public:std::string name;int day;
};class LeaveRequest {
public:bool HandleRequest(const Context &ctx) {if (ctx.day <= 1)HandleByBeaty(ctx);if (ctx.day <= 3)HandleByMainProgram(ctx);else if (ctx.day <= 10)HandleByProjMgr(ctx);elseHandleByBoss(ctx);}private:bool HandleByBeaty(const Context &ctx) {}bool HandleByMainProgram(const Context &ctx) {}bool HandleByProjMgr(const Context &ctx) {}bool HandleByBoss(const Context &ctx) {}
};

符合设计原则:
从单个节点出发
实现一个条件接口和构建链表关系的静态接口

  • 实现处理功能
    在这里插入图片描述
  • 实现链条关系
    在这里插入图片描述
  • 实现功能传递功能
    在这里插入图片描述

实现一个构建链表关系的静态接口
在这里插入图片描述
加入责任链之后的代码:

#include <string>class Context {
public:std::string name;int day;
};// 稳定点 抽象  变化点 扩展 (多态)
//  从单个处理节点出发,我能处理,我处理,我不能处理交给下一个人处理
//  链表关系如何抽象class IHandler {
public:virtual ~IHandler() : next(nullptr) {}void SetNextHandler(IHandler *next) { // 链表关系next = next;}bool Handle(const Context &ctx) {if (CanHandle(ctx)) {return HandleRequest(ctx);} else if (GetNextHandler()) {return GetNextHandler()->Handle(ctx);} else {// err}return false;}// 通过函数来抽象 处理节点的个数  处理节点顺序static bool handler_leavereq(Context &ctx) {IHandler * h0 = new HandleByBeauty();IHandler * h1 = new HandleByMainProgram();IHandler * h2 = new HandleByProjMgr();IHandler * h3 = new HandleByBoss();h0->SetNextHandler(h1);h1->SetNextHandler(h2);h2->SetNextHandler(h3);return h0->Handle(ctx);}
protected:virtual bool HandleRequest(const Context &ctx) {return true};virtual bool CanHandle(const Context &ctx) {return true};IHandler * GetNextHandler() {return next;}
private:IHandler *next; // 组合基类指针
};// 能不能处理,以及怎么处理
class HandleByMainProgram : public IHandler {
protected:virtual bool HandleRequest(const Context &ctx){//怎么处理的return true;}virtual bool CanHandle(const Context &ctx) {//处理条件if (ctx.day <= 10)return true;return false;}
};class HandleByProjMgr : public IHandler {
protected:virtual bool HandleRequest(const Context &ctx){//return true;}virtual bool CanHandle(const Context &ctx) {//if (ctx.day <= 20)return true;return false;}
};
class HandleByBoss : public IHandler {
protected:virtual bool HandleRequest(const Context &ctx){//return true;}virtual bool CanHandle(const Context &ctx) {//if (ctx.day < 30)return true;return false;}
};class HandleByBeauty : public IHandler {
protected:virtual bool HandleRequest(const Context &ctx){//return true;}virtual bool CanHandle(const Context &ctx) {//if (ctx.day <= 3)return true;return false;}
};int main() {// IHandler * h1 = new HandleByMainProgram();// IHandler * h2 = new HandleByProjMgr();// IHandler * h3 = new HandleByBoss();// h1->SetNextHandler(h2);// h2->SetNextHandler(h3);
// 抽象工厂
// nginx http 处理 // 设置下一指针 Context ctx;if (IHander::handler_leavereq(ctx)) {cout << "请假成功";} else {cout << "请假失败";}return 0;
}

设计原则

  • 组合优于继承
  • 面向接口编程
  • 接口依赖

扩展代码

实现处理接口

  • 针对增加节点

修改静态接口

  • 调整顺序
  • 添加节点或删除节点处理

总结

要点

  • 解耦请求方和处理方,请求方不知道请求是如何被处理,处理方的组成是由相互独立的子处理构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合;
  • 责任链请求强调请求最终由一个子处理流程处理;通过了各个子处理条件判断;
  • 责任链扩展就是功能链,功能链强调的是,一个请求依次经由功能链中的子处理流程处理;
  • 将职责以及职责顺序运行进行抽象,那么职责变化可以任意扩展,同时职责顺序也可以任意扩展;

本质
分离职责,动态组合;

结构图
在这里插入图片描述

装饰器

定义

动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活。 —— 《设计模式》GoF

解决问题

稳定点:

  • 顺序无关的增加职责
    变化点:
  • 增加

代码结构

普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能针对不同的职位产生不同的奖金组合;

没有设计模式

// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
class Context {
public:bool isMgr;// User user;// double groupsale;
};class Bonus {
public:double CalcBonus(Context &ctx) {double bonus = 0.0;bonus += CalcMonthBonus(ctx);bonus += CalcSumBonus(ctx);if (ctx.isMgr) {bonus += CalcGroupBonus(ctx);}return bonus;}
private:double CalcMonthBonus(Context &ctx) {double bonus/* = */;return bonus;}double CalcSumBonus(Context &ctx) {double bonus/* = */;return bonus;}double CalcGroupBonus(Context &ctx) {double bonus/* = */;return bonus;}
};int main() {Context ctx;// 设置 ctxBonus *bonus = new Bonus;bonus->CalcBonus(ctx);
}

装饰器模式

#include <iostream>
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
using namespace std;
class Context {
public:bool isMgr;// User user;// double groupsale;
};class CalcBonus {    
public:CalcBonus(CalcBonus * c = nullptr) : cc(c) {}virtual double Calc(Context &ctx) {return 0.0; // 基本工资}virtual ~CalcBonus() {}protected:CalcBonus* cc;
};class CalcMonthBonus : public CalcBonus {
public:CalcMonthBonus(CalcBonus * c) : CalcBonus(c) {}virtual double Calc(Context &ctx) {double mbonus /*= 计算流程忽略*/; return mbonus + cc->Calc(ctx);}
};class CalcSumBonus : public CalcBonus {
public:CalcSumBonus(CalcBonus * c) : CalcBonus(c) {}virtual double Calc(Context &ctx) {double sbonus /*= 计算流程忽略*/; return sbonus + cc->Calc(ctx);}
};class CalcGroupBonus : public CalcBonus {
public:CalcGroupBonus(CalcBonus * c) : CalcBonus(c) {}virtual double Calc(Context &ctx) {double gbnonus /*= 计算流程忽略*/; return gbnonus + cc->Calc(ctx);}
};class CalcCycleBonus : public CalcBonus {
public:CalcCycleBonus(CalcBonus * c) : CalcBonus(c) {}virtual double Calc(Context &ctx) {double gbnonus /*= 计算流程忽略*/; return gbnonus + cc->Calc(ctx);}
};int main() {// 1. 普通员工Context ctx1;CalcBonus *base = new CalcBonus();CalcBonus *cb1 = new CalcMonthBonus(base);//依赖注入的方式CalcBonus *cb2 = new CalcSumBonus(cb1);cb2->Calc(ctx1);//计算所有工资// 2. 部门经理Context ctx2;CalcBonus *cb3 = new CalcGroupBonus(cb1);cb3->Calc(ctx2);
}

实现职责功能
在这里插入图片描述
protected 组合基类指针
在这里插入图片描述

通过继承基类扩展功能
在这里插入图片描述
通过依赖注入累加功能
在这里插入图片描述

设计原则

  • 组合优于继承
  • 面向接口编程
  • 接口依赖

扩展代码

  • 继承基类扩展功能
  • 通过依赖注入累加功能

总结

要点

  • 通过采用组合而非继承的手法, 装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • 不是解决“多子类衍生问题”问题,而是解决“父类在多个方向上的扩展功能”问题;
  • 装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能;

什么时候使用?
不影响其他对象的情况下,以动态、透明的方式给对象添加职责;每个职责都是完全独立的功能,彼此之间没有依赖;
本质
动态组合
结构图
在这里插入图片描述

组合模式

定义

将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

解决问题

稳定点
“具备层次关系”稳定的
对象和组合对象可统一使用
变化点
对象的职责变更
组合对象里对象数量变更

什么时候使用组合模式?

  • 如果你想表示对象的部分-整体层次结构,可以选用组合模式,
  • 把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也容易;
  • 如果你希望统一地使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能;

这种设计模式再游戏开发中特别常见。例如每一个用户玩家都有很多系统,比如说签到系统,宠物系统…。这些系统都会绑定在这个用户身上。然后这些宠物系统还有很多小功能。

代码结构

接口用于整合整体和部分的差异
在这里插入图片描述
叶子节点用于实现具体职责
在这里插入图片描述
组合节点职责委托叶子节点实现,同时具备组合叶子节点职责(最后执行的时候要给叶子节点执行)
在这里插入图片描述

class IComponent
{
public:IComponent(/* args */);~IComponent();virtual void Execute() = 0;//执行virtual void AddChild(IComponent *ele) {}virtual void RemoveChild(IComponent *ele) {}
};class Leaf : public IComponent
{
public:virtual void Execute() {cout << "leaf exxcute" << endl;}
};class Composite : public IComponent
{
private:std::list<IComponent*> _list;
public:virtual void AddChild(IComponent *ele) {// ...}virtual void RemoveChild(IComponent *ele) {// ...}virtual void Execute() {//执行的时候要传递到子节点来执行for (auto iter = _list.begin(); iter != _list.end(); iter++) {iter->Execute();}}
};

设计原则

  • 组合优于继承
  • 面向接口编程
  • 接口依赖

扩展代码

  • 继承接口
  • 实现职责
  • 组合整体和部分的关系
    怎么实现?
    将叶子节点当成特殊的组合对象看待,从而统一叶子对象和组合对象;
    在这里插入图片描述

相关文章:

C++设计模式-结构型设计模式

写少量的代码来应对未来需求的变化。 单例模式 定义 保证一个类仅有一个实例&#xff0c;并提供一个该实例的全局访问点。——《设计模式》GoF 解决问题 稳定点&#xff1a; 类只有一个实例&#xff0c;提供全局的访问点&#xff08;抽象&#xff09; 变化点&#xff1a…...

open-webui+ollama本地部署Llama3

前言 Meta Llama 3 是由 Meta 公司发布的下一代大型语言模型&#xff0c;拥有 80 亿和 700 亿参数两种版本&#xff0c;号称是最强大的开源语言模型。它在多个基准测试中超越了谷歌的 Gemma 7B 和 Mistral 7B Instruct 模型。 安装 1.gpt4all https://github.com/nomic-ai/…...

个人对行为型设计模式的理解 @by_TWJ

目录 1. 访问者模式2. 迭代器模式3. 观察者模式4. 模板模式5. 状态模式6. 备忘录模式7. 策略模式8. 解释器模式9. 责任链模式10. 命令模式11. 中介者模式 研究了一下&#xff0c;我为了方便理解&#xff0c;对它们进行了分类&#xff1a; 针对请求者与执行者关系方面 1对多 - 责…...

苹果挖走大量谷歌人才,建立神秘人工智能实验室;李飞飞创业成立「空间智能」公司丨 RTE 开发者日报 Vol.197

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…...

行业唯一!易保全牵头编制的《区块链数据访问安全技术通则》发布

近日&#xff0c;易保全牵头参与编制的《区块链数据访问安全技术通则》经中国国际经济技术合作促进会正式发布&#xff0c;为中国区块链数据安全技术提供了标准参考。 易保全作为牵头起草单位、易保全CEO兼董事长刘刚担任主要起草人参与其中&#xff0c;在区块链技术安全风险…...

Rust Rocket创建第一个hello world的Web程序 Rust Rocket开发常用网址和Rust常用命令

一、Rust Rocket简介 Rust Rocket 是一个用 Rust 语言编写的 Web 应用框架&#xff0c;它结合了 Rust 的安全性和性能优势&#xff0c;以及 Web 开发的便利性。以下是 Rust Rocket 框架的一些优点&#xff1a; 安全性&#xff1a;Rust 是一种注重安全性的编程语言&#xff0c;…...

第G9周:ACGAN理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 上一周已经给出代码&#xff0c;需要可以跳转上一周的任务 第G8周&#xff1a;ACGAN任…...

Linux网络部分——DNS域名解析服务

目录 1. 域名结构 2. 系统根据域名查找IP地址的过程 3.DNS域名解析方式 4.DNS域名解析的工作原理【☆】 5.域名解析查询方式 6.搭建主从DNS域名服务器 ①初始化操作主服务器和从服务器&#xff0c;安装BIND软件 ②修改主服务器的主配置文件、区域配置文件、区域数…...

预处理详解

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 点击主页&#xff1a;optimistic_chen和专栏&#xff1a;c语言&#xff0c; 创作不易&#xff0c;大佬们点赞鼓…...

Python的创建和使用自定义模块

Python 的模块是组织代码的基本单元&#xff0c;它可以包含变量、函数、类等&#xff0c;并且可以被其他 Python 程序引用和重用。除了使用 Python 提供的标准库和第三方库外&#xff0c;开发者还可以创建自定义模块&#xff0c;用于组织和管理自己的代码。本文将详细介绍如何创…...

Python根据预设txt生成“你画我猜”题目PPT(素拓活动小工具)

Python根据预设txt生成“你画我猜”题目PPT&#xff08;素拓活动小工具&#xff09; 场景来源 去年单位内部的一次素拓活动&#xff0c;分工负责策划设置其中的“你画我猜”环节&#xff0c;网络上搜集到题目文字后&#xff0c;想着如何快速做成对应一页一页的PPT。第一时间想…...

小程序地理位置接口权限直接抄作业

小程序地理位置接口有什么功能&#xff1f; 随着小程序生态的发展&#xff0c;越来越多的小程序开发者会通过官方提供的自带接口来给用户提供便捷的服务。但是当涉及到地理位置接口时&#xff0c;却经常遇到申请驳回的问题&#xff0c;反复修改也无法通过&#xff0c;给的理由也…...

【Osek网络管理测试】[TG3_TC6]等待总线睡眠状态_2

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT在满足进入等待睡眠状态的条件时是否进入该状态 …...

BEV下统一的多传感器融合框架 - FUTR3D

BEV下统一的多传感器融合框架 - FUTR3D 引言 在自动驾驶汽车或者移动机器人上&#xff0c;通常会配备许多种传感器&#xff0c;比如&#xff1a;光学相机、激光雷达、毫米波雷达等。由于不同传感器的数据形式不同&#xff0c;如RGB图像&#xff0c;点云等&#xff0c;不同模态…...

c#和python的flask接口的交互

一、灰度图像的传输 c#端的传输 //读入文件夹中的图像 Mat img2 new Mat(file, ImreadModes.AnyColor); //将图像的数据转换成和相机相同的buffer数据 byte[] image_buffer new byte[img2.Width * img2.Height]; int cn img2.Channels(); //通道数 if (cn 1){//将图像的数…...

Python测试框架Pytest的参数化详解

上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不断攀升。 在实际工作中&#xff0c;许多测试用例都是类似的重复&#xff0c;一个个写最后代码会显得很冗余。这里&#xff0c;我们来了解一下pytest.mark.parametrize装饰器&…...

KernelSU 如何不通过模块,直接修改系统分区

刚刚看了术哥发的视频,发现kernelSU通过挂载OverlayFS实现无需模块,即可直接修改系统分区,很是方便,并且安全性也很高,于是便有了这篇文章。 下面的教程与原视频存在差异,建议观看原视频后再结合本文章进行操作。 在未进行修改前,我们打开/system/文件夹,并在里面创建…...

红日靶场ATTCK 1通关攻略

环境 拓扑图 VM1 web服务器 win7&#xff08;192.168.22.129&#xff0c;10.10.10.140&#xff09; VM2 win2003&#xff08;10.10.10.135&#xff09; VM3 DC win2008&#xff08;10.10.10.138&#xff09; 环境搭建 win7&#xff1a; 设置内网两张网卡&#xff0c;开启…...

CellMarker | 人骨骼肌组织细胞Marker大全!~(强烈建议火速收藏!)

1写在前面 分享一下最近看到的2篇paper关于骨骼肌组织的细胞Marker&#xff0c;绝对的Atlas级好东西。&#x1f44d; 希望做单细胞的小伙伴觉得有用哦。&#x1f60f; 2常用marker&#xff08;一&#xff09; general_mrkrs <- c( MYH7, TNNT1, TNNT3, MYH1, MYH2, "C…...

游戏名台词大赏

文章目录 原神&#xff08;圈内&#xff09; 崩坏&#xff1a;星穹铁道&#xff08;圈内&#xff09; 崩坏3&#xff08;圈内&#xff09; 原神 只要不失去你的崇高&#xff0c;整个世界都会为你敞开。 总会有地上的生灵&#xff0c;敢于直面雷霆的威光。 谁也没有见过风&…...

OpenCV如何在图像中寻找轮廓(60)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何模板匹配(59) 下一篇 :OpenCV检测凸包(61) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::findContours使用 OpenCV 函数 cv::d rawContours …...

java 泛型题目讲解

泛型的知识点 泛型仅存在于编译时期&#xff0c;编译期间JAVA将会使用Object类型代替泛型类型&#xff0c;在运行时期不存在泛型&#xff1b;且所有泛型实例共享一个泛型类 public class Main{public static void main(String[] args){ArrayList<String> list1new Arra…...

pptx 文件版面分析-- python-pptx(python 文档解析提取)

安装 pip install python-pptx -i https://pypi.tuna.tsinghua.edu.cn/simple --ignore-installedpptx 解析代码实现 from pptx import Presentation file_name "rag_pptx/test1.pptx" # 打开.pptx文件 ppt Presentation(file_name) for slide in ppt.slides:#pr…...

http的basic 认证方式

写在前面 本文看下http的basic auth认证方式。 1&#xff1a;什么是basic auth认证 basic auth是一种http协议规范中的一种认证方式&#xff0c;即一种证明你就是你的方式。更进一步的它是一种规范&#xff0c;这种规范是这样子&#xff0c;如果是服务端使用了basic auth认证…...

【信息系统项目管理师练习题】信息系统治理

IT治理的核心是关注以下哪项内容? a) 人员培训和发展计划 b) IT定位和信息化建设与数字化转型的责权利划分 c) 业务流程的绩效管理 d) IT基础设施的优化利用 答案: b) IT定位和信息化建设与数字化转型的责权利划分 IT治理体系框架的组成部分包括以下哪些? a) IT战略目标、IT治…...

RabbitMQ之顺序消费

什么是顺序消费 例如&#xff1a;业务上产生者发送三条消息&#xff0c; 分别是对同一条数据的增加、修改、删除操作&#xff0c; 如果没有保证顺序消费&#xff0c;执行顺序可能变成删除、修改、增加&#xff0c;这就乱了。 如何保证顺序性 一般我们讨论如何保证消息的顺序性&…...

轻松上手的LangChain学习说明书

一、Langchain是什么&#xff1f; 如今各类AI模型层出不穷&#xff0c;百花齐放&#xff0c;大佬们开发的速度永远遥遥领先于学习者的学习速度。。为了解放生产力&#xff0c;不让应用层开发人员受限于各语言模型的生产部署中…LangChain横空出世界。 Langchain可以说是现阶段…...

【论文笔记】Training language models to follow instructions with human feedback A部分

Training language models to follow instructions with human feedback A 部分 回顾一下第一代 GPT-1 &#xff1a; 设计思路是 “海量无标记文本进行无监督预训练少量有标签文本有监督微调” 范式&#xff1b;模型架构是基于 Transformer 的叠加解码器&#xff08;掩码自注意…...

嵌入式交叉编译:x265

下载 multicoreware / x265_git / Downloads — Bitbucket 解压编译 BUILD_DIR${HOME}/build_libs CROSS_NAMEaarch64-mix210-linuxcd build/aarch64-linuxmake cleancmake \-G "Unix Makefiles" \-DCMAKE_C_COMPILER${CROSS_NAME}-gcc \-DCMAKE_CXX_COMPILER${CR…...

一、Redis五种常用数据类型

Redis优势&#xff1a; 1、性能高—基于内存实现数据的存储 2、丰富的数据类型 5种常用&#xff0c;3种高级 3、原子—redis的所有单个操作都是原子性&#xff0c;即要么成功&#xff0c;要么失败。其多个操作也支持采用事务的方式实现原子性。 Redis特点&#xff1a; 1、支持…...

C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等的介绍

文章目录 前言一、为什么存在动态内存管理二、动态内存函数的介绍1. malloc函数2. 内存泄漏3. 动态内存开辟位置4. free函数5. calloc 函数6. realloc 函数7. realloc 传空指针 总结 前言 C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等…...

最近惊爆谷歌裁员

Python团队还没解散完&#xff0c;谷歌又对Flutter、Dart动手了。 什么原因呢&#xff0c;猜测啊。 谷歌裁员Python的具体原因可能是因为公司在进行技术栈的调整和优化。Python作为一种脚本语言&#xff0c;在某些情况下可能无法提供足够的性能或者扩展性&#xff0c;尤其是在…...

音频可视化:原生音频API为前端带来的全新可能!

音频API是一组提供给网页开发者的接口&#xff0c;允许他们直接在浏览器中处理音频内容。这些API使得在不依赖任何外部插件的情况下操作和控制音频成为可能。 Web Audio API 可以进行音频的播放、处理、合成以及分析等操作。借助于这些工具&#xff0c;开发者可以实现自定义的音…...

【中等】保研/考研408机试-动态规划1(01背包、完全背包、多重背包)

背包问题基本上都是模板题&#xff0c;重点&#xff1a;弄熟多重背包模板 dp[j]max(dp[j-v[i]]w[i],dp[j]) //核心思路代码&#xff08;一维数组版&#xff09; dp[i][j]max(dp[i-1][j], dp[i-1][j-v[i]]w[i])//二维数字版 一、 0-1背包 一般输入两个变量&#xff1a;体积&…...

[DEMO]给两个字符串取交集的词语

要求&#xff1a;2个英文字符串中&#xff0c;取相同的大于等于4个字母的词组 比如&#xff1a; 字符串1&#xff1a;" xingMeiLingabcdef WorldHello", 字符串2&#xff1a;"mnjqlup WorldLingLing xingMeiLingHello" 获取交接&#xff1a; [xingMeiLing…...

leetcode53-Maximum Subarray

题目 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xf…...

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之七 简单进行人脸检测并添加面具特效实现

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之七 简单进行人脸检测并添加面具特效实现 目录...

【go项目01_学习记录06】

学习记录 1 使用中间件1.1 测试一下1.2 push代码 2 URI 中的斜杆2.1 StrictSlash2.2 兼容 POST 请求 1 使用中间件 代码中存在重复率很高的代码 w.Header().Set("Content-Type", "text/html; charsetutf-8")统一对响应做处理的&#xff0c;我们可以使用中…...

Vue中Element的下载

打开vscode让项目在终端中打开 输入npm install element-ui2.15.3 然后进行下载 在node_modules中出现element-ui表示下载完成 然后在输入Vue.use(ElementUI); import Vue from vue import App from ./App.vue import router from ./router import ElementUI from element-ui…...

机器人项目相关

机器人项目相关 1. Nvidia 1.1 Jetson 1.1.1 初步安装Riva教程 llamaspeakJetson AGX Orin踩坑记录&#xff08;1&#xff09;安装Riva 参考知乎链接&#xff1a;https://zhuanlan.zhihu.com/p/670007305 1.1.2 NVIDIA Jetson AI Lab 借助 NVIDIA Jetson™ 将生成式 AI…...

Mac升级go版本某种错误情况处理

当看到 "go1.21 is keg-only, which means it was not symlinked into /opt/homebrew" 这样的信息时&#xff0c;意味着Homebrew没有自动为你创建指向新版本Go的符号链接&#xff08;symlink&#xff09;&#xff0c;因为这是一个旧版本Go的替代版本。 Homebrew中的…...

美团KV存储squirrel和Celler学习

文章目录 美团在KV存储squirrel优化和改进在水平方向1、对Gossip协议进行优化 在垂直扩展方面1、forkless RDB数据复制优化2、使用多线程&#xff0c;充分利用机器的多核能力 在高可用方面 美团持久化kv存储celler优化和改进水平扩展优化1、使用bulkload进行数据导入2、线程模型…...

Python学习笔记------处理数据和生成折线图

给定数据&#xff1a; jsonp_1629344292311_69436({"status":0,"msg":"success","data":[{"name":"美国","trend":{"updateDate":["2.22","2.23","2.24",&qu…...

知识图谱与大语言模型的协同(RAG)——MindMap

MindMap : Knowledge Graph Prompting Sparks Graph of Thoughts in Large Language Models 论文地址: https://arxiv.org/abs/2308.09729 代码:https://github.com/wylwilling/MindMap 1.概述 大型语言模型(LLMs)在处理新信息、防止生成幻觉内容、以及增强决策过程透明度…...

奶爸预备 |《P.E.T.父母效能训练:让亲子沟通如此高效而简单:21世纪版》 / 托马斯·戈登——读书笔记

目录 引出致中国读者译序前言第1章 父母总是被指责&#xff0c;而非受训练第2章 父母是人&#xff0c;不是神第3章 如何听&#xff0c;孩子才会说&#xff1a;接纳性语言第4章 让积极倾听发挥作用第5章 如何倾听不会说话的婴幼儿第6章 如何听&#xff0c;孩子才肯听第8章 通过改…...

【WebGIS实例】(13)MapboxGL 加载地形高程数据

前言 官网示例&#xff1a;Add 3D terrain to a map | Mapbox GL JS | Mapbox 大佬博客&#xff1a;Mapbox GL基础&#xff08;七&#xff09;&#xff1a;地形数据的处理与加载 (jl1mall.com) 加载Mapbox地形数据 map.once(style.load, () > {map.addSource(mapbox-dem,…...

Node.js -- MongoDB

文章目录 1. 相关介绍2. 核心概念3. 命令行交互3.1数据库命令3.2 集合命令3.3 文档命令 4. 数据库应用场景4.1 新增4.2 删除4.3 更新4.4 查询 5. 图形化工具Robo 3T 1. 相关介绍 一、简介 Mongodb是什么 MongoDB是一个基于分布式文件存储的数据库&#xff0c;官方地址https://…...

语音识别--单声道转换与降采样

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…...

基于springboot+vue+Mysql的点餐平台网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

数据库优化

一、主从读写分离 主库:主要负责数据的写入。 从库:主要负责数据的查询。 引出问题: 可能会存在主从延迟,导致主从一致性问题。查询主库的量级需要控制。数据量庞大,索引也占据存储空间,磁盘空间不足,当主库宕机后会影响所有模块的写入,需要进行数据分片,因此引出分库…...

Java测试框架:分享常用的Java测试框架,如JUnit, TestNG等,包括单元测试,集成测试,性能测试等

单元测试框架 JUnit JUnit简介 JUnit是一个开源的Java测试框架,用于编写和执行可重复的测试。它是Java开发人员的一个重要工具,用于进行单元测试、回归测试和模块化测试。JUnit提供了一种形式化的方式来编写测试用例,并通过这些测试用例核实代码的正确性。具有可预测的测试…...

EIP-4844对Polygon的意义

1. 引言 Dencun 升级引入了L2新资源&#xff0c;并降低了与calldata相关的交易手续费。 本文要点为&#xff1a; EIP-4844 引入了 blobspace —— 一种用于高效管理大型二进制对象 (large binary objects&#xff0c;BLOB) 的数据库存储概念。Blobs 将降低rollups的交易成本…...

docker镜像容器常用命令

常用基础命令1、docker info #查看docker版本等信息 2、docker search jenkins #搜索jenkins镜像 3、docker history nginx #查看镜像中各层内容及大小,每层对应的dockerfile中的一条指令。 4、docker network ls #显示当前主机上的所有网络 5、docker logs nginx …...

MySql-分页查询

分页查询 – 1、从起始索引0 开始查询员工数据&#xff0c;每页展示5条记录 – select * from tb_emp limit 0,5; – 2、查询第2页员工数据&#xff0c;每页展示5条记录 – select * from tb_emp limit 5,5; – 3、查询第二页员工数据&#xff0c;每页展示6条记录 – sel…...

【MIT 6.5840(6.824)学习笔记】使用Go进行线程和RPC编程

1 为什么选择Go 在实现分布式系统时&#xff0c;选择合适的编程语言非常重要。Go有以下特点&#xff1a; 优秀的线程支持&#xff1b;便捷的RPC机制、类型&#xff1b;内存安全以及垃圾回收机制。 这使Go成为了一个理想的选择。Go不仅相对简单&#xff0c;而且其垃圾回收机制…...

Github 2024-05-12 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Filament: 加速Laravel开发的完美起点 创建周期:1410 天开发语言:PHP协议类型:MIT LicenseStar数量:12228 个Fork数量:1990 次关…...