一、设计模式是什么
- 设计模式是指在软件开发中,经过验证 的,用于解决在特定环境 下,重复出现 的,特定问题的解决方案。
- 解决问题的固定套路。
- 慎用设计模式。
二、设计模式是怎么来的
- 满足设计原则 后,慢慢迭代出来的。
三、设计模式解决了什么问题
- 前提:具体需求既有稳定点 ,又有变化点。
- 期望修改少量的代码,就可以适应需求的变化。
- 比喻:整洁的房间,好动的猫,怎么保证房间的整洁 → 把猫关在一个笼子里。
四、设计模式基础
- 面向对象的思想:
- 封装:隐藏实现细节,实现模块化。
- 继承:无需修改原有类的情况下通过继承实现对功能的扩展。
- 多态:
- 静态多态:函数重载。
- 动态多态:继承中虚函数重写。
- 设计原则:
- 依赖倒置:
- 代码实现要依赖接口,接口又可以转化为抽象,也就是代码实现要依赖抽象,用户使用也需要依赖抽象。
- 用户使用和代码实现的解耦,也就是接口的使用者不依赖具体的实现 。
- 开闭原则:对扩展开放,对修改关闭。封装、多态。
- 面向接口:封装。
- 封装变化点:封装、多态。
- 单一职责:封装。
- 里氏替换:多态。
- 接口隔离。
- 组合优于继承。
- 最小知道原则:封装。
- 依赖倒置:
五、设计模式学习步骤
- 该设计模式解决了什么问题 → 稳定点 、变化点。
- 该设计模式的代码结构是什么。
- 该设计模式符合哪些设计原则。
- 如何在该设计模式上扩展代码。
- 该设计模式有哪些典型应用场景。
六、模板方法
- 解决的问题:
- 稳定点:算法的骨架。
- 变化点:子流程需要变化。
- 符合哪些设计原则:
- 单一职责:只暴露算法的骨架接口,其它的子流程接口都用
protected
或private
封装起来。 - 开闭原则:不能改变算法的骨架,只能修改子流程。
- 依赖倒置。
- 子类扩展时,需要依赖基类的虚函数实现。
- 使用者只依赖接口。
- 封装变化点:变化的地方通过
protected
暴露给子类去扩展。 - 接口隔离:通过
protected
、private
隔离接口。 - 最小知道原则:对于用户而言,只需要知道算法的骨架接口即可。
- 单一职责:只暴露算法的骨架接口,其它的子流程接口都用
- 如何扩展:
- 实现子类继承基类,复写子流程。
- 通过多态调用方式使用。
- 代码结构:
-
基类中有骨架流程接口。
-
所有子流程对子类开放并且是虚函数。
-
通过多态调用方式使用。
cpp// 背景:某个动物园,有一套固定的表演流程,但是其中若干个表演子流程可替换,以迭代更新表演流程。 /* * 接口隔离原则:1、类封装,使用权限限定词来实现。 2、类与类依赖,使用接口实现(依赖注入) * 最小知道原则 * 单一职责原则:类封装 * 开闭原则:对扩展开放,对修改关闭 * 里氏替换原则:多态 * * 扩展方式:继承、多态组合 */ using namespace std; class ZooShow { public: void Show() { if (Show0()) { PlayGame(); } Show1(); Show2(); Show3(); } private: void PlayGame() { cout << "after Show0, then play game" << endl; } // 对其它用户关闭,但是对子类开放 protected: bool expired; virtual bool Show0() { cout << "Show0" << endl; if (!expired) { return true; } return false; } virtual void Show1() { cout << "Show1" << endl; } virtual void Show2() { cout << "Show2" << endl; } virtual void Show3() { cout << "Show3" << endl; } }; // 模板方法 class ZooShowEx1: public ZooShow { protected: virtual bool Show0() { cout << "Show0 Ex1" << endl; if (!expired) { // 里氏替换原则 return true; } return false; } }; class ZooShowEx2: public ZooShow { protected: virtual void Show2() { cout << "Show2 E2" << endl; } }; class ZooShowEx3: public ZooShow { protected: virtual bool Show0() { cout << "Show0 Ex3" << endl; if (!expired) { return true; } return false; } virtual void Show2() { cout << "Show2 Ex3" << endl; } }; int main() { std::cout << "Template Method!" << std::endl; // 第一次迭代 ZooShow * zs1 = new ZooShowEx1; // 晚绑定 zs1->Show(); // 第二次迭代 ZooShow * zs2 = new ZooShowEx2; zs2->Show(); // 第三次迭代 ZooShow * zs3 = new ZooShowEx3; zs3->Show(); return 0; }
-
七、观察者模式
-
解决的问题:
- 稳定点:"一" 对 "多" 的依赖关系,"一" 变化,"多" 跟着变化。
- 变化点:"多" 增加、"多" 减少。
-
符合哪些设计原则:
- 面向接口编程。
- 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
- 用户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
- 减少系统中各部分的依赖关系,从而实现高内聚、低耦合的类型设计方案。
- 接口隔离:类与类的依赖建立在一个接口上。
- 容器存储接口。
- 依赖注入。
- 封装变化点:通过
Attach
和Detach
接口来封装变化点。
- 面向接口编程。
-
如何扩展:
- 继承实现接口。
- 调用
Attach
、Detach
。
-
代码结构:
cpp// 背景:气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到三个不同的显示终端 // "一" 指的是数据中心,"多" 指的是不同的显示终端 using namespace std; class IDisplay { public: virtual void Show(float temperature) = 0; virtual ~IDisplay() {} }; class DisplayA: public IDisplay { public: virtual void Show(float temperature) { cout << "DisplayA show" << endl; } }; class DisplayB: public IDisplay { public: virtual void Show(float temperature) { cout << "DisplayB show" << endl; } }; class DisplayC: public IDisplay { virtual void Show(float temperature) { cout << "DisplayC show" << endl; } }; class DataCenter { public: // 依赖注入 void Attach(IDisplay * ob) { obs.push_back(ob); } // 依赖注入 void Detach(IDisplay * ob) { obs.remove(ob); } void notify() { float temperature = CalcTemperature(); for (auto ob : obs) { ob->Show(temperature); } } private: float CalcTemperature() { // 获取计算得到的温度 float temperature = 20.f; return temperature; } // 容器存储接口 std::list<IDisplay*> obs; }; int main() { // 单例模式 DataCenter *dataCenter = new DataCenter; // 某个模块 DisplayA *dA = new DisplayA; dataCenter->Attach(dA); DisplayB *dB = new DisplayB; dataCenter->Attach(dB); DisplayC *dC = new DisplayC; dataCenter->Attach(dC); dataCenter->notify(); dataCenter->Detach(dB); dataCenter->notify(); }
八、策略模式
-
解决的问题:
- 稳定点:用户程序与算法的调用关系。
- 变化点:算法内容改变、新加算法。
-
符合哪些设计原则:
- 接口隔离:类与类的依赖建立在一个接口上。
- 依赖注入。
- 面向接口编程。
- 开闭原则。
- 接口隔离:类与类的依赖建立在一个接口上。
-
代码结构:
cpp// 背景:商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格。 // 稳定点:抽象去解决它 // 变化点:扩展(继承、多态的组合)去解决它 using namespace std; class Context { }; class ProStrategy { public: virtual double CalcPro(const Context &ctx) = 0; }; class Fes_Spring: public ProStrategy { public: virtual double CalcPro(const Context &ctx) { cout << "Fes_Spring 0.8" << endl; return 0.8; } }; class Fes_Common: public ProStrategy { public: virtual double CalcPro(const Context &ctx) { cout << "Fes_Common 0.5" << endl; return 0.7; } }; class Promotion { public: // 依赖注入 Promotion(ProStrategy *s) { s_ = s; } ~Promotion() {} double CalcPromotion(const Context &ctx) { return s_->CalcPro(ctx); } private: ProStrategy *s_; }; int main() { Context ctx; ProStrategy *fs = new Fes_Spring; Promotion *pm = new Promotion(fs); pm->CalcPromotion(ctx); ProStrategy * fm = new Fes_Common; Promotion *pm2 = new Promotion(fm); pm2->CalcPromotion(ctx); return 0; }
九、单例模式
-
解决的问题:
- 稳定点:一个类仅有一个实例,并提供一个该实例的全局访问点。
- 变化点:有多个类都是单例,能不能复用代码。
-
代码结构:
- 私有的构造函数和析构函数。
- 禁掉拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。
- 静态类成员函数。
- 静态私有成员变量。
-
版本一。
cpp/* * 存在问题: * 1、程序退出时,指针 _instance 会被释放,但是它所指向的堆内存不会被释放,所以 _instance 的析构函数是不会被调用的 * 如果该单例需要操作文件,操作完毕后要调用析构函数释放文件资源,由于析构函数不会被调用,就会产生 bug */ class Singleton { public: static Singleton * GetInstance() { if (_instance == nullptr) { _instance = new Singleton(); } return _instance; } private: Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } Singleton(const Singleton &) = delete; // 拷贝构造 Singleton& operator=(const Singleton &) = delete; // 拷贝赋值构造 Singleton(Singleton &&) = delete; // 移动构造 Singleton& operator=(Singleton &&) = delete; // 移动赋值构造 static Singleton * _instance; // 在静态全局区分配 }; Singleton* Singleton::_instance = nullptr; // 静态成员需要初始化 int main() { Singleton * s = Singleton::GetInstance(); return 0; }
-
版本二。
cpp/* * 存在问题: * 1、不支持多线程 * 2、atexit() 是线程安全的,但是 GetInstance() 不是线程安全的 */ class Singleton { public: static Singleton * GetInstance() { if (_instance == nullptr) { _instance = new Singleton(); // 多线程情况下,会产生资源竞争,可能会创建多个对象 atexit(Destructor); // 当程序退出时,调用 Destructor 函数 } return _instance; } private: static void Destructor() { if (nullptr != _instance) { delete _instance; _instance = nullptr; } } Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } Singleton(const Singleton &) = delete; Singleton& operator=(const Singleton &) = delete; Singleton(Singleton &&) = delete; Singleton& operator=(Singleton &&) = delete; static Singleton * _instance; }; Singleton* Singleton::_instance = nullptr; int main() { Singleton * s = Singleton::GetInstance(); return 0; }
-
版本三:懒汉模式 。
-
在对象需要被使用时才进行初始化,而不是在程序启动时,也就是延迟初始化,实例在第一次被使用时创建 → 减少程序启动时的负载和内存占用。
-
通过双重检测 解决多线程问题。
cpp/* * 存在问题: * C++98 的语义是基于单线程的,使用的是标准外的一些库,比如 pthread_create 等。 * C++11 在其基础上进行了封装,封装了线程操作,比如 mutex、atomic、内存栅栏,用来解决多线程问题 * 在多核时代,会对程序进行一些优化: * 1. 编译器重排 * 2. CPU 重排 * 这样有可能会违反顺序一致性,导致一些问题 * 1. 可见性问题 * 2. 执行序问题 * C++11 提供了一些解决方法 * 1. 同步原语(原子变量 + 内存栅栏)(无锁编程) * 2. 锁 */ class Singleton { // 懒汉模式 public: static Singleton * GetInstance() { /* * 双重检测解决多线程问题: * 第一次访问这个接口的时候才会涉及到资源竞争,需要加锁(写操作) * 第二次访问这个接口的时候,不涉及资源竞争,不需要加锁(读操作) */ if (_instance == nullptr) { // RAII:在类的生命周期进行资源管理 std::lock_guard<std::mutex> lock(_mutex); if (_instance == nullptr) { _instance = new Singleton(); /* * new 是一个操作符 * (1)分配内存 * (2)调用构造函数 * (3)返回指针 * 多线程环境下,CPU 会进行指令重排(reorder) * 有可能先调用(1)、(3),再调用(2),导致要操作对象数据的时候,对象数据还没有初始化,产生异常 */ atexit(Destructor); } } return _instance; } private: static void Destructor() { if (nullptr != _instance) { delete _instance; _instance = nullptr; } } Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } 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; // 互斥锁初始化 int main() { Singleton * s = Singleton::GetInstance(); return 0; }
-
-
版本四:饿汉模式 。
-
单例实例在类被加载到内存时就立即初始化,这意味着无论这个实例后续是否被用到,它都会被创建。
-
因为实例在类加载时完成初始化,所以实例的创建是线程安全的。
cppclass Singleton { // 饿汉模式 public: // 提供静态方法访问实例 static Singleton& GetInstance() { return instance; } private: Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } Singleton(const Singleton &) = delete; Singleton& operator=(const Singleton &) = delete; Singleton(Singleton &&) = delete; Singleton& operator=(Singleton &&) = delete; static Singleton instance; // 静态成员变量,类加载时初始化 }; Singleton Singleton::instance; int main() { Singleton& s = Singleton::GetInstance(); return 0; }
-
-
版本五。
cpp/* * 原子变量解决问题: * 1、原子执行的问题:同时只有一个线程去执行这条语句 * 内存栅栏解决问题: * 2、可见性问题:通过 store(修改数据让其它线程可见) 和 load(可以看见其它线程最新操作的数据)解决 * 3、执行序问题:内存模型 * memory_order_acquire:下面的代码不能优化到上面去 * memory_order_release:上面的代码不能优化到下面来 */ class Singleton { public: static Singleton * GetInstance() { // 解决原子执行问题、可见性问题、执行序问题 Singleton * tmp = _instance.load(std::memory_order_acquire); if (tmp == nullptr) { std::lock_guard<std::mutex> lock(_mutex); tmp = _instance.load(std::memory_order_acquire); if (tmp == nullptr) { tmp = new Singleton; _instance.store(tmp, std::memory_order_release); atexit(Destructor); } } return tmp; } static Singleton * GetInstance2() { // 如果 Singleton 构造函数中又用到了原子变量,需要进行拆分 // 解决原子执行问题、可见性问题(std::memory_order_relaxed => 内存结构是松散的结构,不限制优化,效率最高) 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); 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() { if (nullptr != _instance) { delete _instance; _instance = nullptr; } } Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } 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; int main() { Singleton * s = Singleton::GetInstance(); return 0; }
-
版本六。
cpp/* * C++11 magic static 特性:如果变量在初始化的时候并发同时进入声明语句,并发线程将会阻塞等待初始化结束 * 1、利用静态局部变量特性,延迟加载 * 2、利用静态局部变量特性,系统自动回收内存,自动调用析构函数 * 3、静态局部变量初始化时,没有 new 操作带来的 cpu 指令重排(reorder) * 4、C++11 静态局部变量初始化时,具备线程安全 */ class Singleton { public: static Singleton& GetInstance() { static Singleton instance; return instance; } private: Singleton() { cout << "Singleton" << endl; } ~Singleton() { cout << "~Singleton" << endl; } Singleton(const Singleton &) = delete; Singleton& operator=(const Singleton &) = delete; Singleton(Singleton &&) = delete; Singleton& operator=(Singleton &&) = delete; }; int main() { Singleton& s = Singleton::GetInstance(); return 0; }
-
最终版本。
cpptemplate<typename T> class Singleton { public: static T& GetInstance() { // 这里要初始化 DesignPattern,需要调用 DesignPattern 的构造函数,同时会调用父类的构造函数(先调用) static T instance; return instance; } protected: virtual ~Singleton() { cout << "~Singleton" << endl; } // protected 修饰构造函数,才能让别人继承 Singleton() { cout << "Singleton" << endl; } 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>; // 让基类能够调用子类的构造函数 private: DesignPattern() { cout << "DesignPattern" << endl; } ~DesignPattern() { cout << "~DesignPattern" << endl; } }; int main() { DesignPattern &dp = DesignPattern::GetInstance(); return 0; }
十、工厂模式
- 定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使得一个类的实例化延迟到子类。
- 为什么要有工厂模式,而不直接使用
new
?- 因为除了
new
,还有复杂的构造流程。 - 工厂模式是为了解决过程比较复杂,希望对外隐藏这些细节的场景。
- 比如连接池、线程池。
- 隐藏对象真实类型。
- 创建对象有复杂的依赖关系。
- 因为除了
- 解决的问题:
- 稳定点:创建同类对象的接口(对象创建接口)、同类对象有一个相同的职责(功能接口)。
- 变化点:创建对象的扩展。
- 符合哪些设计原则:
- 最小知道原则。
- 面向接口编程。
- 如何扩展:
- 实现对象创建接口。
- 实现功能接口。
- 多态调用。
- 代码结构:
-
对象创建接口 → 创建具体对象,调用一个功能接口。
-
实现一个功能接口 。
cpp// 背景: 实现一个导出数据的接口,让客户选择数据的导出方式。 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) { cout << data << endl; return true; } }; class ExportJson: public IExport { public: virtual bool Export(const std::string &data) { cout << data << endl; return true; } }; class ExportTxt : public IExport { public: virtual bool Export(const std::string &data){ cout << data << endl; 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: virtual IExport * NewExport(/* ... */) { IExport * temp = new ExportTxt(); return temp; } }; int main() { IExportFactory * factory = new ExportTxtFactory(); factory->Export("export txt"); return 0; }
-
十一、抽象工厂
- 定义:提供一个接口,让该接口负责创建一些列 "相关或者相互依赖的对象",无需指定它们具体的类。
- 解决的问题:
- 稳定点:创建同类对象的接口,同类对象有多个相同的职责。
- 变化点:创建对象的扩展。
- 工厂模式和抽象工厂的区别 :
- 工厂模式一个对象只有一个职责,而抽象工厂一个对象有多个职责。
- 代码结构:
-
对象创建接口 → 创建具体对象,调用多个功能接口。
-
实现多个功能接口 。
cpp// 实现一个导出导入数据的接口,让客户选择数据的导出导入方式。 class IObj { public: virtual bool Export(const std::string &data) = 0; virtual bool Import(const std::string &data) = 0; virtual ~IObj(){} }; class XmlObj : public IObj { public: virtual bool Export(const std::string &data) { cout << data << endl; return true; } virtual bool Import(const std::string &data) { cout << data << endl; return true; } }; class JsonObj: public IObj { public: virtual bool Export(const std::string &data) { cout << data << endl; return true; } virtual bool Import(const std::string &data) { cout << data << endl; return true; } }; class TxtObj : public IObj { public: virtual bool Export(const std::string &data){ cout << data << endl; return true; } virtual bool Import(const std::string &data) { cout << data << endl; return true; } }; class IDataApiFactory { public: IDataApiFactory() { _obj = nullptr; } virtual ~IDataApiFactory() { if(_obj) { delete _obj; _obj = nullptr; } } bool Export(const std::string &data) { if (_obj == nullptr) { _obj = NewObj(); } return _obj->Export(data); } bool Import(const std::string &data) { if (_obj == nullptr) { _obj = NewObj(); } return _obj->Import(data); } protected: virtual IObj * NewObj(/* ... */) = 0; private: IObj* _obj; }; class XmlApiFactory : public IDataApiFactory { protected: virtual IObj * NewObj(/* ... */) { IObj * temp = new XmlObj(); return temp; } }; class JsonApiFactory : public IDataApiFactory { protected: virtual IObj * NewObj(/* ... */) { IObj * temp = new JsonObj(); return temp; } }; class TxtApiFactory : public IDataApiFactory { protected: virtual IObj * NewObj(/* ... */) { IObj * temp = new TxtObj(); return temp; } }; int main() { IDataApiFactory * jsonObj = new JsonApiFactory(); jsonObj->Import("import json"); jsonObj->Export("export json"); IDataApiFactory * txtObj = new TxtApiFactory(); txtObj->Import("import txt"); txtObj->Export("export txt"); return 0; }
-
十二、责任链
- 定义:使多个节点都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些节点连成一条链,并沿着这条链传递请求,直到有一个节点处理它为止。
- 解决的问题:
- 稳定点:处理流程 → 请求按照链条进行传递(链表关系 、类型是接口),只要有一个节点处理了,就会返回,
- 变化点:处理节点的个数、处理顺序、处理条件。
- 扩展代码:
- 实现处理接口。
- 修改静态接口。
- 符合哪些设计原则:
- 组合优于继承。
- 面向接口编程。
- 接口依赖。
- 代码结构:
-
从单个节点出发,实现处理接口,实现一个构建链表关系的静态接口。
cpp// 背景:请假流程,1 天内需要组长批准,3 天内需要项目经理批准,3 天以上需要老板批准 class Context { public: std::string name; int day; }; // 从单个处理节点出发,我能处理,我处理,我不能处理,交给下一个人处理 // 链表关系如何抽象 class IHandler { public: IHandler(): _next(nullptr) {} virtual ~IHandler() {} 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; } protected: virtual bool HandleRequest(const Context &ctx) = 0; virtual bool CanHandle(const Context &ctx) = 0; IHandler * GetNextHandler() { return _next; } private: IHandler * _next; // 组合基类指针 }; // 能不能处理,以及怎么处理 class HandleByLeader: public IHandler { protected: virtual bool HandleRequest(const Context &ctx){ return true; } virtual bool CanHandle(const Context &ctx) { if (ctx.day <= 1){ 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 <= 3){ 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 Request { public: Request() { IHandler * h0 = new HandleByLeader(); IHandler * h1 = new HandleByProjMgr(); IHandler * h2 = new HandleByBoss(); h0->SetNextHandler(h1); h1->SetNextHandler(h2); handler = h0; } bool Handle(const Context& ctx) { return handler->Handle(ctx); } private: IHandler *handler; }; int main() { Context ctx; ctx.day = 31; auto req = std::make_unique<Request>(); bool state = req->Handle(ctx); if (state) { cout << "success" << endl; } else { cout << "failure" << endl; } return 0; }
-
十三、装饰器
-
定义:动态地给一个对象增加一些额外的职责,就增加功能而言,装饰器模式比生产子类更为灵活。
-
解决的问题:
- 稳定点:顺序无关地增加职责。
- 变化点:增加职责。
-
符合哪些设计原则:
- 组合优于继承。
- 面向接口编程。
- 接口依赖。
-
代码结构:
cpp// 背景:普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金,后面可能会增加其他奖金,同时可能针对不同的职位产生不同的奖金组合。 class Context { public: bool isMgr; }; class CalcBonus { public: CalcBonus(CalcBonus * c = nullptr): cc(c) { if (cc != nullptr) { cout << "CalcBonus" << endl; _salary = cc->_salary; } } virtual double Calc(Context &ctx) { return _salary; } virtual ~CalcBonus() {} protected: CalcBonus* cc; double _salary = 10.0; }; class CalcMonthBonus : public CalcBonus { public: CalcMonthBonus(CalcBonus *c) : CalcBonus(c) { cout << "CalcMonthBonus" << endl; }; virtual double Calc(Context & ctx) { double mbonus = 100.0; // 忽略计算过程 return mbonus + cc->Calc(ctx); } }; class CalcSumBonus : public CalcBonus { public: CalcSumBonus(CalcBonus *c) : CalcBonus(c) { cout << "CalcSumBonus" << endl; }; virtual double Calc(Context & ctx) { double sbonus = 200.0; // 忽略计算过程 return sbonus + cc->Calc(ctx); } }; class CalcGroupBonus : public CalcBonus { public: CalcGroupBonus(CalcBonus * c) : CalcBonus(c) { cout << "CalcGroupBonus" << endl; }; virtual double Calc(Context &ctx) { double gbonus = 300.0; // 忽略计算过程 return gbonus + cc->Calc(ctx); } }; int main() { // 普通员工 Context ctx_con; CalcBonus *base = new CalcBonus(); CalcBonus *cb1= new CalcMonthBonus(base); CalcBonus *cb2 = new CalcSumBonus(cb1); double con_salary = cb2->Calc(ctx_con); cout << con_salary << endl; // 部门经理 Context ctx_mgr; CalcBonus *cb3 = new CalcGroupBonus(cb2); double mgr_salary = cb3->Calc(ctx_mgr); cout << mgr_salary << endl; return 0; }
十四、组合模式
-
解决的问题:
- 稳定点:层次关系是稳定的,对象和组合对象的使用是一致的。
- 变化点:对象的职责变更,组合对象里对象数量变更。
-
代码结构:
cppclass IComponent { public: IComponent() {}; ~IComponent() {}; virtual void Execute() = 0; virtual void AddChild(IComponent *ele){} virtual void RemoveChild(IComponent *ele){} }; class Leaf : public IComponent { public: virtual void Execute (){ cout << "left execute" << 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(); } } };