C/C++设计模式之结构型设计模式

设计模式学习步骤

(1)设计模式解决什么问题?本质上是分析稳定点和变化点。在开发过程中要抽象稳定的,扩展变化点。设计模式具体解决问题的场景:希望修改少量的代码,就可以适应需求的变化。
(2)设计模式的代码结构是什么?代码结构反映出使用了什么设计模式。
(3)设计模式符合哪些设计原则?因为设计模式是由设计原则推导来的,可以按照设计模式产生的流程重新思考,能够很好的帮助我们去设计代码。
(4)如何在上面扩展代码?
(5)该设计模式有哪些典型应用场景。

学习步骤
设计模式解决什么问题
稳定点
变化点
设计模式的代码结构是什么
设计模式符合哪些设计原则
如何在上面扩展代码
该设计模式有哪些典型应用场景
联系工作场景
开源框架

工厂模式

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。

Factory
+CreateProduct()
Product
+Operator()
ConcreteProduct1
+Operator()
ConcreteProduct2
+Operator()
ConcreteProduct3
+Operator()

解决了什么问题

(1)稳定点:创建同类对象的接口;同类对象有一个相同的职责。
(2)变化点:创建对象的扩展。

要点

解决创建过程比较复杂,希望对外隐藏这些细节的场景。
比如:
(1)连接池、线程池
(2)隐藏对象真实类型;
(3)对象创建会有很多参数来决定如何创建;
(4)创建对象有复杂的依赖关系。

设计原则

(1)最小知道原则。
(2)面向接口原则。

代码结构

(1)对象创建接口。创建具体的对象,调用功能结构。
(2)一个功能接口。
(3)使用方式是晚绑定的方式。

扩展代码

(1)实现对象创建接口
(2)实现功能接口
(3)多态接口

本质

延迟到子类来选择实现。

背景

举个例子:实现一个导出数据的接口,让客户选择数据的导出方式。

#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;
}

抽象工厂模式

定义

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

AbstractFactory
+CreateProductA()
+CreateProductB()
AbstractProductA
+Operator()
AbstractProductB
+Operator()
ConcreteFactory1
+CreateProductA()
+CreateProductB()
ConcreteFactory2
+CreateProductA()
+CreateProductB()
ProductA1
+Operator()
ProductA2
+Operator()
ProductB1
+Operator()
ProductB2
+Operator()

解决了什么问题

(1)稳定点:创建同类对象的接口;同类对象有多个相同的职责。
(2)变化点:创建对象的扩展。

代码结构

(1)对象创建接口。创建具体的对象,提供多个功能结构来调用。
(2)多个功能接口。

背景

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

#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;
}

抽象工厂模式和工厂模式的区别

工厂模式通常一个对象只有一个职责,而抽象工厂模式是一个对象有多个职责。

责任链模式

定义

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

Client
Handler
+HandlerRequest()
ConcreteHandlerA
+HandlerRequest()
ConcreteHandlerB
+HandlerRequest()

解决了什么问题

(1)稳定点:处理流程。请求按照链条传递,只要有一个对象处理了请求,那么就不会继续往下传递(即可打断)。
(2)变化点:处理节点的个数;处理顺序;处理条件。

设计原则

(1)组合由于继承。
(2)面向接口编程。
(3)接口依赖。

代码结构

从单节点出发,实现处理接口,实现一个构建链表关系的静态接口。

扩展代码

(1)实现处理接口。
(2)修改静态接口。

要点

(1)解耦请求方和处理方,请求方不知道请求是如何被处理,处理方的组成是由相互独立的子处理构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合;
(2)责任链请求强调请求最终由一个子处理流程处理;通过了各个子处理条件判断;
(3)责任链扩展就是功能链,功能链强调的是,一个请求依次经由功能链中的子处理流程处理;
(4)将职责以及职责顺序运行进行抽象,那么职责变化可以任意扩展,同时职责顺序也可以任意扩展。

本质

分离职责,动态组合;

背景

举个例子:请求流程,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 <= 1)
            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>
#include <iostream>
using namespace std;
class Context {
public:
	std::string name;
	int day;
};



// 稳定点 抽象  变化点 扩展 (多态)
//  从单个处理节点出发,我能处理,我处理,我不能处理交给下一个人处理
//  链表关系如何抽象

class IHandler {
public:
	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);
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 {
public:
	HandleByMainProgram() {};
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) {
		//
		cout << "HandleByProjMgr" << endl;
		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) {
		//
		cout << "HandleByBoss" << endl;
		if (ctx.day < 30)
			return true;
		return false;
	}
};

class HandleByBeauty : public IHandler {
public:
	HandleByBeauty() {};
protected:
	virtual bool HandleRequest(const Context &ctx) {
		//
		return true;
	}
	virtual bool CanHandle(const Context &ctx) {
		//
		cout << "HandleByBeauty" << endl;
		if (ctx.day <= 3)
			return true;
		return false;
	}
};


// 通过函数来抽象 处理节点的个数  处理节点顺序
bool IHandler::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);
}

int main() {
	// 抽象工厂
	// nginx http 处理 
	// 设置下一指针 
	Context ctx;
	if (IHandler::handler_leavereq(ctx)) {
		cout << "请假成功";
	}
	else {
		cout << "请假失败";
	}
	getchar();

	return 0;
}

装饰器模式

定义

动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活。

ConcreteComponent
+Operation()
Component
+Operation()
Decorator
+Operation()
ConcreteDecorator
+Operation()
+AddedBehavior()

解决了什么问题

(1)稳定点:顺序无关的增加职责。使用组合的方式。
(2)变化点:不断增加职责。

设计原则

(1)组合由于继承。
(2)面向接口编程。
(3)接口依赖。

代码结构

通过组合基类指针的方式,与顺序无关。

扩展代码

继承基类,然后实现自己的职责。

要点

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

什么时候使用?

不影响其他对象的情况下,以动态、透明的方式给对象添加职责;每个职责都是完全独立的功能,彼此之间没有依赖。

本质

动态组合

背景

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

#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);
}

组合模式

定义

将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

解决了什么问题

(1)稳定点:层次关系稳定的;对象和组合对象使用要一致。层次关系用list容器存储。
(2)变化点:对象(叶子节点)的职责变更;组合对象里的对象数量变更。

组合模式在游戏开发中使用非常频繁。

代码结构

利用一个公共类来消除叶子节点(叶子节点主要实现功能职责)和组合节点(组合节点主要委托给叶子节点执,另外还有添加节点和删除节点的功能)的差异。

扩展代码

(1)叶子节点继承公共类,实现它的职责。
(2)新加一个对象时,直接调用新增功能;移除对象时,直接调用移除功能。

什么时候使用组合模式?

(1)如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也容易。
(2)如果你希望统一地使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。

怎么实现?

将叶子节点当成特殊的组合对象看待,从而统一叶子对象和组合对象。

Client
Leaf
+Operation()
Component
+Operation()
+Add(Component)
+Remove(Component)
+GetChild(int)
Composite
+Operation()
+Add(Component)
+Remove(Component)
+GetChild(int)
Compositeo

总结

单例模式、工厂模式、抽象工厂模式、责任链模式、组合模式、装饰器模式是比较常用的结构型设计模式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值