设计模式简谈

news/2024/11/22 10:28:08/

设计模式是我们软件架构开发中不可缺失的一部分,通过学习设计模式,我们可以更好理解的代码的结构和层次。

设计原则

设计原则是早于设计方法出现的,所以的设计原则都要依赖于设计方法。这里主要有八个设计原则。

推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:链接

  • 依赖倒置

    • 高层模块不应该依赖低层模块,两者都应该依赖抽象
    • 抽象不应该依赖具体实现,具体实现应该依赖于抽象

    这个怎么理解呢? 第一点是上层的模块,例如直接给用户使用的API, 不能直接暴露底层的实现给用户, 要通过中间的一层抽象接口。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlstJ76s-1682246752955)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230403234456184.png)]

例如: 自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变动;自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依赖自动驾驶行业标准(抽象);

  • 开放封闭

    • 一个类应该对扩展(组合和继承)开放,对修改关闭;

    类中的接口应该对需要组合/继承的类暴露接口,对需要常变的封装到private里面。

  • 面向接口

    • 不将变量类型声明为某个特定的具体类,而是声明为某个接口;
    • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
    • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案
  • 封装变化点

    • 将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离;
  • 单一职责

    • 一个类应该仅有一个引起它变化的原因
  • 里氏替换

    • 子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;
  • 接口隔离

    • 不应该强迫客户依赖于它们不用的方法;
    • 一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
    • 客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上
  • 组合优于继承

    • 继承耦合度高,组合耦合度低

1. 模板方法

定义:定义一个操作中的算法的骨架 ,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

要点:

  • 最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;
  • 反向控制流程的典型应用;
  • 父类 protected 保护子类需要复写的子流程;这样子类的子流程只能父类来调用;

结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kWhZKNlu-1682246752956)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230405110539617.png)]

我们可以看到,父类是一个抽象类,父类的接口由子类来复写实现。

  • 我们先来看下没有用模板方法写的代码。

    我们顶一个了类zoo,里面的实现由show0, show1, show2…, 如果想添加一个新的show的,则需要在类zoo里面添加,这样就破坏了 类zoo的封装, 加入类zoo我们不想暴露给用户去修改呢?

#include <iostream>
using namespace std;
class ZooShow {
public:ZooShow(int type = 1) : _type(type) {}public:void Show() {if (Show0())PlayGame();Show1();Show2();Show3();}private:void PlayGame() {cout << "after Show0, then play game" << endl;}bool Show0() {if (_type == 1) {// return true;} else if (_type == 2 ) {//  ...} else if (_type == 3) {}cout << _type << " show0" << endl;return true;}void Show1() {if (_type == 1) {cout << _type << " Show1" << endl;} else if (_type == 2) {cout << _type << " Show1" << endl;} else if (_type == 3) {}}void Show2() {if (_type == 20) {}cout << "base Show2" << endl;}void Show3() {if (_type == 1) {cout << _type << " Show1" << endl;} else if (_type == 2) {cout << _type << " Show1" << endl;}}
private:int _type;
};int main () {ZooShow *zs = new ZooShow(1);bzs->Show();return 0;
}
  • 使用模板方法

    我们把父类的接口固定,然后定义若干个子类去继承父类的接口。在使用时只需定义父类的指针,指向要实现的子类的对象即可。

#include <iostream>
using namespace std;
// 开闭
class ZooShow {
public:void Show() {// 如果子表演流程没有超时的话,进行一个中场游戏环节;如果超时,直接进入下一个子表演流程if (Show0())PlayGame();Show1();Show2();Show3();}private:void PlayGame() {cout << "after Show0, then play game" << endl;}bool expired;// 对其他用户关闭,但是子类开放的
protected:virtual bool Show0() {cout << "show0" << endl;if (! expired) {return true;}return false;}virtual void Show2() {cout << "show2" << endl;}virtual void Show1() {}virtual void Show3() {}
};// 框架
// 模板方法模式
class ZooShowEx10 : public ZooShow {
protected:virtual void Show0() {if (! expired) {return true;}return false;}
}class ZooShowEx1 : public ZooShow {
protected:virtual bool Show0() {cout << "ZooShowEx1 show0" << endl;if (! expired) { // 里氏替换return true;}return false;}virtual void Show2(){cout << "show3" << endl;}
};class ZooShowEx2 : public ZooShow {
protected:virtual void Show1(){cout << "show1" << endl;}virtual void Show2(){cout << "show3" << endl;}
};class ZooShowEx3 : public ZooShow {
protected:virtual void Show1(){cout << "show1" << endl;}virtual void Show3(){cout << "show3" << endl;}virtual void Show4() {//}
};
/*
*/
int main () {ZooShow *zs = new ZooShowEx10; // 晚绑定// ZooShow *zs1 = new ZooShowEx1;// ZooShow *zs2 = new ZooShowEx2;zs->Show();return 0;
}

2. 观察者模式

定义: 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

要点:

  • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合;
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了;
  • 观察者不要依赖通知顺序,目标对象也不知道通知顺序;
  • 常用在基于事件的ui框架中,也是 MVC 的组成部分;
  • 常用在分布式系统中、actor框架中;

结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdIHfpLb-1682246752956)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230405113324563.png)]

首先我们有一个抽象的父类,然后几个具体实现的子类, 一旦我们observer由任何的变化,我们就需要对每个子类的进行通知。

  • 没有使用观察者模式

可以看到,我们定义n多个子类去接收CalcTemperature的改变。

class DisplayA {
public:void Show(float temperature);
};class DisplayB {
public:void Show(float temperature);
};class DisplayC {
public:void Show(float temperature);
}class WeatherData {
};class DataCenter {
public:void TempNotify() {DisplayA *da = new DisplayA;DisplayB *db = new DisplayB;DisplayC *dc = new DisplayC;// DisplayD *dd = new DisplayD;float temper = this->CalcTemperature();da->Show(temper);db->Show(temper);dc->Show(temper);dc->Show(temper);}
private:float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}WeatherData * GetWeatherData(); // 不同的方式
};int main() {DataCenter *center = new DataCenter;center->TempNotify();return 0;
}
  • 观察者模式
#include <list>
#include <algorithm>
using namespace std;
//
class IDisplay {
public:virtual void Show(float temperature) = 0;virtual ~IDisplay() {}
};class DisplayA : public IDisplay {
public:virtual void Show(float temperature) {cout << "DisplayA Show" << endl;}
private:void jianyi();
};class DisplayB : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayB Show" << endl;}
};class DisplayC : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayC Show" << endl;}
};class DisplayD : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayC Show" << endl;}
};class WeatherData {
};// 应对稳定点,抽象
// 应对变化点,扩展(继承和组合)
class DataCenter {
public:void Attach(IDisplay * ob) {//}void Detach(IDisplay * ob) {//}void Notify() {float temper = CalcTemperature();for (auto iter : obs) {iter.Show(temper);}}// 接口隔离
private:WeatherData * GetWeatherData();float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}std::list<IDisplay*> obs;
};int main() {// 单例模式DataCenter *center = new DataCenter;// ... 某个模块IDisplay *da = new DisplayA();center->Attach(da);// ...IDisplay *db = new DisplayB();center->Attach(db);IDisplay *dc = new DisplayC();center->Attach(dc);center->Notify();//-----center->Detach(db);center->Notify();//....center->Attach(dd);center->Notify();return 0;
}

3. 策略模式

定义: 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。

要点:

  • 策略模式提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换;
  • 策略模式消除了条件判断语句;也就是在解耦合;

结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6GQqjjpJ-1682246752957)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230405114314169.png)]

策略模式的本质是用来消除多余的if else的。

  • 没有使用策略模式

    enum VacationEnum {VAC_Spring,VAC_QiXi,VAC_Wuyi,VAC_GuoQing,VAC_ShengDan,
    };class Promotion {VacationEnum vac;
    public:double CalcPromotion(){if (vac == VAC_Spring {// 春节}else if (vac == VAC_QiXi) {// 七夕}else if (vac == VAC_Wuyi) {// 五一}else if (vac == VAC_GuoQing) {// 国庆}else if (vac == VAC_ShengDan) {}}};
    
  • 使用策略模式

class Context {};// 稳定点:抽象去解决它
// 变化点:扩展(继承和组合)去解决它
class ProStategy {
public:virtual double CalcPro(const Context &ctx) = 0;virtual ~ProStategy(); 
};
// cpp
class VAC_Spring : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1  : public VAC_QiXi {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};class VAC_Shengdan : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};class Promotion {
public:Promotion(ProStategy *sss) : s(sss){}~Promotion(){}double CalcPromotion(const Context &ctx){return s->CalcPro(ctx);}
private:ProStategy *s;
};int main () {Context ctx;ProStategy *s = new VAC_QiXi1();Promotion *p = new Promotion(s);p->CalcPromotion(ctx);return 0;
}

4. 工厂模式

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

主要是为了创建同类对象的接口, 同类对象只有一个相同的职责。

看一个例子, 没有用工厂模式, 我们需要使用大量的if, else 来判断什么类型的时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oz3hK5H9-1682246752957)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230423103315209.png)]

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

使用工厂模式:用IExportFactory把接口创建出来, 真正实现延迟到子类实现。

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

1) 如果为每一个具体的 ConcreteProduct 类的实例化提供一个函数体, 那么我们可能不得不在系统中添加了一个方法来处理这个新建的 ConcreteProduct,这样 Factory 的接口永远就不肯能封闭( Close)。 当然我们可以通过创建一个 Factory 的子类来通过多态实现这一点,但是这也是以新建一个类作为代价的。

2)在实现中我们可以通过参数化工厂方法,即给 FactoryMethod( )传递一个参数用以 决定是创建具体哪一个具体的 Product。当
然也可以通过模板化避免 1)中的子类创建子类,其方法就是将具体 Product 类作为模板参数,实现起来也很简单。可以看出, Factory 模式对于对象的创建给予开发人员提供了很好的实现策略,但是Factory 模式仅仅局限于一类类(就是说 Product 是一类,有一个共同的基类),如果我们要为不同类的类提供一个对象创建的接口,那就要用 AbstractFactory 了。

5. 抽象工厂

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

主要是为了创建同类对象的接口,和工厂模式最主要的区分是,同类对象具有很多相同的职责

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkKakX3A-1682246752958)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230423103258779.png)]

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

AbstractFactory 模式和 Factory 模式的区别是初学(使用)设计模式时候的一个容易引起困惑的地方。 实际上, AbstractFactory 模式是为创建一组( 有多类) 相关或依赖的对象提供创建接口, 而 Factory 模式正如我在相应的文档中分析的是为一类对象提供创建接口或延
迟对象的创建到子类中实现。并且可以看到, AbstractFactory 模式通常都是使用 Factory 模式实现( ConcreteFactory1)。

6. 责任链模式

定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 这个思路很容易理解,一条链上有若干个请求,每个请求都有可能被每一个节点处理,处理完可能就不需要后面的来处理了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77pZxcV3-1682246752958)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230423104152894.png)]

Chain of Responsibility 模式中 ConcreteHandler 将自己的后继对象(向下传递消息的对象)记录在自己的后继表中,当一个请求到来时, ConcreteHandler 会先检查看自己有没有匹配的处理程序, 如果有就自己处理, 否则传递给它的后继。 当然这里示例程序中为了简化,ConcreteHandler 只是简单的检查看自己有没有后继,有的话将请求传递给后继进行处理,没有的话就自己处理。

#include <iostream>
using namespace std;
class Handle
{
public:virtual ~Handle();virtual void HandleRequest() = 0;void SetSuccessor(Handle* succ) { _succ = succ; }Handle* GetSuccessor() { return _succ; }
protected:Handle() {	_succ = NULL; }Handle(Handle* succ) { this->_succ = succ; }
private:Handle* _succ;
};
class ConcreteHandleA:public Handle
{
public:ConcreteHandleA();~ConcreteHandleA();ConcreteHandleA(Handle* succ) : Handle(succ) {}void HandleRequest(){if (this->GetSuccessor() != 0) {cout<<"ConcreteHandleA request to next ....."<<endl;this->GetSuccessor()->HandleRequest();} else {cout<<"ConcreteHandleA handle"<<endl;}}
protected:
private:
};class ConcreteHandleB:public Handle
{
public:ConcreteHandleB();~ConcreteHandleB();ConcreteHandleB(Handle* succ):: Handle(succ) {}void HandleRequest() {if (this->GetSuccessor() != 0) {cout<<"ConcreteHandleA request to next ....."<<endl;this->GetSuccessor()->HandleRequest();} else {cout<<"ConcreteHandleA handle"<<endl;}}
protected:
private:
};int main() {Handle* h1 = new ConcreteHandleA();Handle* h2 = new ConcreteHandleB();h1->SetSuccessor(h2);h1->HandleRequest();return 0;
}

Chain of Responsibility 模式的示例代码实现很简单,这里就其测试结果给出说明:ConcreteHandleA 的对象和 h1 拥有一个后继 ConcreteHandleB 的对象 h2,当一个请求到来时候, h1 检查看自己有后继,于是 h1 直接将请求传递给其后继 h2 进行处理, h2 因为没有后继,当请求到来时候,就只有自己提供响应了。

Chain of Responsibility 模式的最大的一个有点就是给系统降低了耦合性, 请求的发送者完全不必知道该请求会被哪个应答对象处理,极大地降低了系统的耦合性。

7 装饰器模式

在 OO 设计和开发过程, 可能会经常遇到以下的情况: 我们需要为一个已经定义好的类添加新的职责(操作), 通常的情况我们会给定义一个新类继承自定义好的类,这样会带来一个问题( 将在本模式的讨论中给出)。通过继承的方式解决这样的情况还带来了系统的复
杂性,因为继承的深度会变得很深。而 Decorator 提供了一种给类增加职责的方法,不是通过继承实现的,而是通过组合。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-USBk6hf2-1682246752958)(C:\Users\zhen\AppData\Roaming\Typora\typora-user-images\image-20230423110230354.png)]

在 结 构 图 中 , ConcreteComponent 和 Decorator 需 要 有 同 样 的 接 口 , 因 此ConcreteComponent 和 Decorator 有着一个共同的父类。这里有人会问,让 Decorator 直接维护一个指向 ConcreteComponent 引用(指针) 不就可以达到同样的效果, 答案是肯定并且是否定的。 肯定的是你可以通过这种方式实现, 否定的是你不要用这种方式实现, 因为通过这种方式你就只能为这个特定的 ConcreteComponent 提供修饰操作了,当有了一个新的ConcreteComponent 你又要去新建 一 个 Decorator 来 实 现 。 但是通过结构图中的ConcreteComponent 和 Decorator 有一个公共基类, 就可以利用 OO 中多态的思想来实现只要是 Component 型别的对象都可以提供修饰操作的类,这种情况下你就算新建了100个 。Component 型别的类 ConcreteComponent,也都可以由 Decorator 一个类搞定。这也正是Decorator 模式的关键和威力所在了。当然如果你只用给 Component 型别类添加一种修饰, 则 Decorator 这个基类就不是很必
要了。

没有用装饰器

// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 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;// 设置 ctxBonus *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);
}

http://www.ppmy.cn/news/52651.html

相关文章

Spring IOC之对象的创建方式、策略及销毁时机和生命周期且获取方式

目录 一、对象的创建方式 1. 使用构造方法 2. 使用工厂类方法 3. 使用工厂类的静态方法 二、对象的创建策略 1. 单例策略 2. 多例策略 三、对象的销毁时机 四、生命周期方法 1. 定义生命周期方法 2. 配置生命周期方法 3. 测试 五、获取Bean对象的方式 1. 通过id/…

参与C++项目时的那些事儿

开发工具 在开发团队内部&#xff0c;使用相同的IDE、编译器等开发工具&#xff0c;工具的版本号和配置保持一致&#xff0c;便于开发团队积累使用经验&#xff0c;避免、消除工具的差异引入的问题。 代码质量 从检查时机看&#xff0c;分为&#xff1a; 开发人员本地检查&am…

以轻量级服务器niginx为核心的JavaWeb项目:第一章 项目设计

这里写目录标题 一 需求分析与环境搭建1.需求分析2.环境搭建1.2.1首先配置mysql环境1.2.2 配置maven环境 二 打成War包&#xff0c;发到linux上 一 需求分析与环境搭建 1.需求分析 2.环境搭建 1.2.1首先配置mysql环境 先查找一下mysql环境 [roothadoop122 ~]# mysql --vers…

vue---mixin混入

mixins是一种分发 Vue 组件中可复用功能的非常灵活的方式。混合对象可以包含任意组件选项。当组件使用混合对象时&#xff0c;所有混合对象的选项将被混入该组件本身的选项。 一个混入对象可以包含任意组件选项&#xff08;如data、methods、created、mounted等等&#xff09;。…

Stable Diffusion XL:更快,更强

Stable Diffusion XL&#xff1a;更快&#xff0c;更强 今天&#xff0c;Stability AI 的创始人兼首席执行官 Emad Mostaque 发推宣布&#xff0c;Stable Diffusion XL 进入公测阶段。 核心信息总结起来有2点&#xff1a; “XL”不是新模型的官方名称&#xff0c;Stability …

【cuda】Nsight System 下载,安装与使用

Nsight System 下载 nsys 是 NVIDIA Nsight Systems 的命令行工具&#xff0c;可以用于分析 CUDA 应用程序的性能和行为。以下是在 Linux 上安装 nsys 的步骤&#xff1a; 下载 NVIDIA Nsight Systems 安装程序。您可以在 NVIDIA 的官方网站上下载适用于您的系统的安装程序。…

手推A Unified Solution to Constrained Bidding in Online Display Advertising论文

A Unified Solution to Constrained Bidding in Online Display Advertising&#xff1a;一种对在线展示广告约束出价问题的通用解决方案 未开放但是可以搜到 NeuralAuction: 电商广告中的端到端机制优化方法 https://arxiv.org/abs/2106.03593 一种使用真负样本的在线延迟反…

【LPC55s69】使用FAL分区管理与easyflash变量管理

文章目录 1.FAL组件1.1什么是FAL1.2 使用ENV配置FAL1.3 FAL SFUD 移植1.4 FAL SFUD 测试用例1.5 测试结果 2.DFS文件系统2.1 什么是DFS2.2 DFS架构2.3 使用ENV配置DFS2.4 DFS挂载到FAL分区测试2.5 测试结果 3.Easyflash移植到FAL分区3.1 简述EasyFlash3.2EasyFlash软件包使用3.…