常用设计模式(工厂方法,抽象工厂,责任链,装饰器模式)

前言

有关设计模式的其他常用模式请参考
单例模式的实现
常见的设计模式(模板与方法,观察者模式,策略模式)

工程方法

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。 ------《设计模式》GoF

要点

解决创建过程比较复杂,希望对外隐藏这些细节的场景;

  • 比如连接池、线程池
  • 隐藏对象真实类型;
  • 对象创建会有很多参数来决定如何创建;
  • 创建对象有复杂的依赖关系;

本质

延迟到子类来选择实现;

结构图

举例

实现一个导出数据的接口,让客户选择数据的导出方式;

代码

cpp 复制代码
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

其实和工厂方法是类似的,只是在工厂中创建多个对象。

结构图

例子

实现一个拥有导出导入数据的接口,让客户选择数据的导出导入方式;

代码

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) {
        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 天以上需要老板批准;

在前面链接的说明了设计模式主要是设计需求的变化点,稳定点是固定的流程。

在这里:稳定点 是处理流程是稳定的(首选判断是否能够处理,如果不能处理,则交给更高层处理,可以抽象出是否能够处理接口和处理接口)。变化点包括:天数,职责人个数(后续可能会增加更多的项目职责)。

代码

cpp 复制代码
#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

要点

cpp 复制代码
- 通过采用组合而非继承的手法, 装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的"灵活性差"和"多子类衍生问题"。
- 不是解决"多子类衍生问题"问题,而是解决"父类在多个方向上的扩展功能"问题;
- 装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能;

本质

动态组合

例子

普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能针对不同的职位产生不同的奖金组合;

代码

cpp 复制代码
#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);
}
相关推荐
杨充20 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充20 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充20 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充20 小时前
2.面向对象的特性
设计模式
杨充20 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充20 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充20 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式