前言
有关设计模式的其他常用模式请参考
单例模式的实现
常见的设计模式(模板与方法,观察者模式,策略模式)
工程方法
定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。 ——《设计模式》GoF
要点
解决创建过程比较复杂,希望对外隐藏这些细节的场景;
- 比如连接池、线程池
- 隐藏对象真实类型;
- 对象创建会有很多参数来决定如何创建;
- 创建对象有复杂的依赖关系;
本质
延迟到子类来选择实现;
结构图
举例
实现一个导出数据的接口,让客户选择数据的导出方式;
代码
class IExport
{
protected:
IExport() {}
virtual ~IExport() {}
public:
virtual void exportFile() = 0;
};
class JSONExport :public IExport
{
public:
JSONExport() {}
~JSONExport() {}
void exportFile()
{
std::cout << "export json file" << std::endl;
}
};
class TxtFileExport :public IExport
{
public:
TxtFileExport() {}
~TxtFileExport() {}
void exportFile()
{
std::cout << "export txt file" << std::endl;
}
};
class XMLExport :public IExport
{
public:
XMLExport() {}
~XMLExport() {}
void exportFile()
{
std::cout << "export xml file" << std::endl;
}
};
class IFactoryMethodExport
{
public:
virtual IExport* createExport() = 0;//创建比较复杂,使用一个函数来创建
};
class FactoryMethodJSONExport :public IFactoryMethodExport
{
public:
IExport* createExport() {
//比较多的参数初始化
jsonExport = new JSONExport();//这里没有使用参数这些
//其他初始化操作
}
private:
JSONExport* jsonExport;
};
class FactoryMethodXMLExport :public IFactoryMethodExport
{
public:
IExport* createExport() {
//比较多的参数初始化
xmlExport = new XMLExport();//这里没有使用参数这些
//其他初始化操作
}
private:
XMLExport* xmlExport;
};
class FactoryMethodTxtFileExport :public IFactoryMethodExport
{
public:
IExport* createExport() {
//比较多的参数初始化
txtFileExport = new TxtFileExport();//这里没有使用参数这些
//其他初始化操作
return txtFileExport;
}
private:
TxtFileExport* txtFileExport;
};
抽象工厂
定义
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。——《设计模式》GoF
其实和工厂方法是类似的,只是在工厂中创建多个对象。
结构图
例子
实现一个拥有导出导入数据的接口,让客户选择数据的导出导入方式;
代码
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;
}
};
// 对于初学者: 知道扩展代码
// 5年
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;
}
责任链模式
定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 ——《设计模式》GoF
要点
- 解耦请求方和处理方,请求方不知道请求是如何被处理,处理方的组成是由相互独- 立的子处理构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合;
- 责任链请求强调请求最终由一个子处理流程处理;通过了各个子处理条件判断;
- 责任链扩展就是功能链,功能链强调的是,一个请求依次经由功能链中的子处理流程处理;
- 将职责以及职责顺序运行进行抽象,那么职责变化可以任意扩展,同时职责顺序也可以任意扩展;
本质
- 分离职责,动态组合;
结构图
例子
请求流程,1 天内需要主程序批准,3 天内需要项目经理批准,3 天以上需要老板批准;
在前面链接的说明了设计模式主要是设计需求的变化点,稳定点是固定的流程。
在这里:稳定点是处理流程是稳定的(首选判断是否能够处理,如果不能处理,则交给更高层处理,可以抽象出是否能够处理接口和处理接口)。变化点包括:天数,职责人个数(后续可能会增加更多的项目职责)。
代码
#include <string>
struct WorkerContext {
std::string name;
int day;
};
class IWorker{
public:
IWorker(WorkerContext& context) :context(context)
{
}
//处理流程是固定的,稳定点不变
bool handle() {
if (isCanHandle()) {//可以处理
return handRequest();
}
else if(next){//不可以处理,交给下个处理者处理
return next->handle();
}
std::cout << "无法处理" << std::endl;
return false;
}
void setNextHandler(IWorker* _next)//设置下一个处理者
{
next = _next;
}
protected:
virtual bool isCanHandle() = 0;//是否可以处理
virtual bool handRequest() = 0;//处理
WorkerContext& context;
private:
IWorker* next = nullptr;//下一个流程处理者
};
//
class MainProgram :public IWorker {
public:
MainProgram(WorkerContext& context) :IWorker(context) {}
private:
bool isCanHandle() {
if (context.day <= 1)
{
return true;
}
return false;
}
bool handRequest() {
std::cout << "MainProgram 同意" << context.name << "天数" << context.day << std::endl;
return true;
}
};
class ProjectManager :public IWorker {
public:
ProjectManager(WorkerContext& context) :IWorker(context) {}
private:
bool isCanHandle() {
if (context.day <= 3)
{
return true;
}
return false;
}
bool handRequest() {
std::cout << "ProjectManager 同意" << context.name << "天数" << context.day << std::endl;
return true;
}
};
class Boss :public IWorker {
public:
Boss(WorkerContext& context) :IWorker(context) {}
private:
bool isCanHandle() {
if (context.day > 3 && context.day < 10)
{
return true;
}
return false;
}
bool handRequest() {
std::cout << "Boss 同意" << context.name << "天数" << context.day << std::endl;
return true;
}
};
int main{
WorkerContext context;
context.day = 10;
context.name = "susan";
IWorker* worker1 = new MainProgram(context);
IWorker* worker2 = new ProjectManager(context);
IWorker* worker3 = new Boss(context);
worker1->setNextHandler(worker2);
worker2->setNextHandler(worker3);
worker1->handle();
}
装饰器模式
定义
动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活。—— 《设计模式》GoF
要点
- 通过采用组合而非继承的手法, 装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
- 不是解决“多子类衍生问题”问题,而是解决“父类在多个方向上的扩展功能”问题;
- 装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能;
本质
动态组合
例子
普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能针对不同的职位产生不同的奖金组合;
代码
#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 *cb2 = new CalcSumBonus(base);
CalcBonus *cb1 = new CalcMonthBonus(cb2);
cb2->Calc(ctx1);
// 2. 部门经理
Context ctx2;
CalcBonus *cb3 = new CalcGroupBonus(cb2);
cb3->Calc(ctx2);
}