结构型设计模式:
一、单例模式:
定义:保证一个类只有一个实例,并提供一个该实例的全局访问接口。
代码举例:
- 版本一
缺点:线程不安全
class Singleton { public: static Singleton *GetInstance() { if (_instance == nullptr){ _instance = new Singleton(); atexit(Destructor); // atexit 是线程安全的 } 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; //静态成员需要初始化
- 版本二
说明:线程安全 懒汉模式
#include <mutex> Singleton *Singleton::_instance = nullptr; //静态成员需要初始化 std::mutex Singleton::_mutex; //互斥锁初始化 class Singleton { public: static Singleton *GetInstance() { // std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程 if (_instance == nullptr) { std::lock_guard<std::mutex> lock(_mutex); // 3.2 if (_instance == nullptr) { _instance = new Singleton(); // 1. 分配内存 2. 调用构造函数 3. 返回指针 // 问题:多线程环境下,会出现CPU指令重排,比如:132 这样如果在没调用构造函数,就返回了指针,操作该指针会报错 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; };
- 版本三
说明:解决多线程情况下,cpu指令重排问题。
同步原语
分为:原子变量和内存栅栏。其中,原子变量负责解决原子行和可见性,内存栅栏负责解决执行序问题。
load 就是可以看见其他线程最新操作的数据。
store 就是让别的线程可以看到自己线程的修改。
static std::atomic<Singleton *> _instance; //原子变量需要添加头文件 #include <mutex> Singleton *tmp = _instance.load(std::memory_order_relaxed); // _instance.store(tmp, std::memory_order_relaxed); //
内存模型
memory_order_acquire 和 memory_order_release之间的代码,不会被优化到他们之外的部分。
memory_order_relaxed 优点:内存模型使用relaxed进行优化,速度快
#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_acquire); //获取内存屏障,也就是开启内存屏障 if (tmp == nullptr) { std::lock_guard<std::mutex> lock(_mutex); tmp =_instance.load(std::memory_order_relaxed); // 内存模型使用relaxed进行优化,速度快 if (tmp == nullptr){ tmp = new Singleton; // 在内存屏障的区域内,乱序没有关系,不会跑出内存屏障的范围 std::atomic_thread_fence(std::memory_order_release); //释放内存屏障 _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; //互斥锁初始化
- 版本四 (最简单的写法)
返回类的局部静态变量的引用。c++11静态局部变量初始化,线程安全
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; //移动拷贝构造 };
- 版本五
具有版本四的所有优点:
a)利用局部静态变量特性,延迟加载;
b)利用局部静态变量特性,系统自动挥手内存,自动调用析构函数;
c)静态局部变量初始化时,没有new操作带来的cpu指令重排问题;
d)c++11静态局部变量初始化,线程安全
为了让基类的Singleton访问到子类DesignPattern的私有构造函数,所以,需要在子类中声明基类为友元类。友元函数可以访问该类的私有成员和属性。
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; //移动拷贝构造 }; // 子类DesignPattern 继承父类 Singleton<DesignPattern> class DesignPattern : public Singleton<DesignPattern> { friend class Singleton<DesignPattern>; //friend 能让Singleton<T> 访问到 DesignPattern构造函数 private : DesignPattern() {} ~DesignPattern() {} };
二、工厂方法
实现代码举例:
#include <string> // 实现导出数据的接口, 导出数据的格式包含 xml,json 后面可能扩展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 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; } }; // 扩展csv格式 /* 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; }
三、抽象工厂方法
代码实现举例
#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 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 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; } }; // 相关性 依赖性 工作当中 int main () { IDataApiFactory *factory = new CSVApiFactory(); factory->Import("hello world"); factory->Export("hello world"); return 0; }
四、责任链
接收者形成一个链条,发送者发送的请求,从第一个开始,往后顺延,直到被处理为止,处理完后面就不会再处理了。简单理解:一个一个传递,有打断功能。
场景:请假流程,1天内需要直系领导审批;3天内需要经理审批,3天以上需要董事长审批。
核心点:我能处理我处理,处理不了往下传。
代码实现举例:
#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 HandleByMainLeader : 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 HandleByMgr : 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() { // nginx http 处理 就是责任连模式 // 设置下一指针 Context ctx; if (IHander::handler_leavereq(ctx)) { cout << "请假成功"; } else { cout << "请假失败"; } return 0; }
五、装饰器模式
定义
动态地给一个对象增加一些额外的职责。比生产子类更加灵活。
什么时候使用
不影响其他对象的情况下,以动态、透明的方式给对象添加职责;每个职责都是完全独立的功能,彼此之间没有依赖
代码实现举例
#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); }
组合模式
定义
将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
什么时候使用
- 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来。
- 如果你希望统一地使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能;
代码实现举例
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/C++linux服务期高级架构。