写少量的代码来应对未来需求的变化。
单例模式
定义
保证一个类仅有一个实例,并提供一个该实例的全局访问点。——《设计模式》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.2
if (_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.2
if (_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);
else
HandleByBoss(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;
// 设置 ctx
Bonus *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();
}
}
};
设计原则
- 组合优于继承
- 面向接口编程
- 接口依赖
扩展代码
- 继承接口
- 实现职责
- 组合整体和部分的关系
怎么实现?
将叶子节点当成特殊的组合对象看待,从而统一叶子对象和组合对象;