工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例
化延迟到子类。 ——《设计模式》
工厂方法用于创建一个导出同类且拥有相同职责对象的接口, 用于创建过程比较复杂并且希望隐藏这些创建细节的情况下。
示例需求
实现一个导出数据的接口,让客户选择数据的导出方式;
#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 IExportFactory {
public:
virtual IExport * NewExport(/* ... */) = 0;
};
class ExportXmlFactory : public IExportFactory {
public:
IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportXml;
// 可能之后有什么操作
return temp;
}
};
class ExportJsonFactory : public IExportFactory {
public:
IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportJson;
// 可能之后有什么操作
return temp;
}
};
class ExportTxtFactory : public IExportFactory {
public:
IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportTxt;
// 可能之后有什么操作
return temp;
}
};
class ExportData {
public:
ExportData(IExportFactory *factory) : _factory(factory) {}
~ExportData() {
if (_factory) {
delete _factory;
_factory = nullptr;
}
}
bool Export(const std::string &data) { // 稳定的流程 往基类放
IExport * e = _factory->NewExport();
e->Export(data);
}
private:
IExportFactory *_factory;
};
int main() {
ExportData ed(new ExportTxtFactory);
ed.Export("hello world");
return 0;
}
代码解释:上述代码中,如果我们希望创建ExportXm, ExportJson或者ExportTxt类的对象并使用其Export方法,我们可以创建一个其对应的工厂类实例并用于初始化总工厂ExportData对象,然后就可以通过ExportData实例来使用你需要的类的方法。当ExportXm, ExportJson或者ExportTxt的创建非常复杂时,这样做可以为客户调用接口减轻负担,同时隐藏创建的细节。注意:这个示例代码中还使用了模板方法模式。
代码结构
为每一个子类建立一个对应的工厂子类,这些工厂子类实现同一个抽象工厂接口。
抽象工厂模式
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
——《设计模式》
抽象工厂是工厂方法模式的一种变种,为了缩减工厂实现子类的数量,不必给每一个产品分配一个工厂类,可以将产品进行分组,每组中的不同产品由同一个工厂类的不同方法来创建。
示例需求
实现一个拥有导出导入数据的接口,让客户选择数据的导出导入方式
#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;
}
相比于普通工厂,这里把工厂分为了Xml, Txt, Json,CSV几类,每一类的对象使用同一工厂进行创建,比如TxtImport和TxtExport用同一个工厂创建出来。极大的缩减了工厂的数量。
责任链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 ——《设计模式》
责任链用于实现这样的情况,有一个发送者和多个接收者,使用责任链可以把接收者串成一条链,接收者们按照顺序去处理发送的信息,且处理流程可以打断。
示例需求
请求流程,1 天内需要主程序批准,3 天内需要项目经理批准,3 天以上需要老板批准;
#include <string>
class Context {
public:
std::string name;
int day;
};
class IHandler {
public:
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 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 * h0 = new HandleByBeauty();
IHandler * h1 = new HandleByMainProgram();
IHandler * h2 = new HandleByProjMgr();
IHandler * h3 = new HandleByBoss();
h0->SetNextHandler(h1);
h1->SetNextHandler(h2);
h2->SetNextHandler(h3);
// 设置下一指针
Context ctx;
h0->Handle(ctx);
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 *cb2 = new CalcSumBonus(base);
CalcBonus *cb1 = new CalcMonthBonus(cb2);
cb2->Calc(ctx1);
// 2. 部门经理
Context ctx2;
CalcBonus *cb3 = new CalcGroupBonus(cb2);
cb3->Calc(ctx2);
}
通过继承来重写父类的接口,再使用组合的方式来扩展功能,比如CalcBonus *cb2 = new CalcSumBonus(base); cb2对象的Calc职责就是CalcSumBonus + CalcBonus的组合职责,使用这种方式扩展了其功能。