设计模式简谈

news2024/11/16 3:50:13

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

设计原则

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

推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[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;
    // 设置 ctx
    Bonus *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.coloradmin.cn/o/452549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阳光万里,祝你上岸——免统考在职研究生

什么是在职研究生 在职研究生&#xff0c;是国家计划内&#xff0c;以在职人员身份&#xff0c;部分时间在职工作&#xff0c;部分时间在校学习的研究生教育的一种类型。在职攻读硕士方式有三种&#xff1a; 1.双证非全日制研究生&#xff1a;为普通高等教育研究生学历&#x…

QGIS--开发OpenSCENARIO动态场景(三)--制作动态场景

一、添加scenario&#xff0c;carla的环境变量 export CARLA_ROOT/path/to/your/carla/installation export SCENARIO_RUNNER_ROOT/path/to/your/scenario/runner/installation export PYTHONPATH$PYTHONPATH:${CARLA_ROOT}/PythonAPI/carla/dist/carla-<VERSION>.egg ex…

如何利用工时表来帮助项目管理做得更完善?

项目管理是一项复杂的任务&#xff0c;需要协调各种资源以确保项目按时交付。其中一个关键方面是管理各个员工工时。工时表软件是一种可以帮助企业记录各个员工工作时效的工具&#xff0c;而且还可以帮助项目管理者记录和跟踪项目成员的时间。那么如何利用工时表来帮助项目管理…

如何配置静态路由?这个实例详解交换机的静态路由配置

一、什么是静态路由 静态路由是一种路由的方式&#xff0c;它需要通过手动配置。静态路由与动态路由不同&#xff0c;静态路由是固定的&#xff0c;不会改变。一般来说&#xff0c;静态路由是由网络管理员逐项加入路由表&#xff0c;简单来说&#xff0c;就是需要手动添加的。…

相空间相关概念以及轨迹生成

在时间序列分析中&#xff0c;相位轨迹也被广泛应用于提取隐藏在数据中的结构信息。例如&#xff0c;在人类活动识别任务中&#xff0c;通过构建不同活动对应的相位轨迹&#xff0c;可以揭示活动间的相似性和差异性&#xff0c;从而有助于设计有效的分类方法。 相空间&#xff…

Java每日一练(20230420)

目录 1. 罗马数字转整数 &#x1f31f; 2. 电话号码的字母组合 &#x1f31f;&#x1f31f; 3. 排列序列 &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每…

java基础——Map

Map 第一章 Map集合 1.1 概述 Java提供了专门的集合类用来存放键值对关系的对象&#xff0c;即java.util.Map接口。 我们通过查看Map接口描述&#xff0c;发现Map接口下的集合与Collection接口下的集合&#xff0c;它们存储数据的形式不同&#xff0c;如下图。 Collection中…

Java学习之匿名内部类(非常重要)

目录 一、匿名内部类介绍 基本语法 二、举例说明 1.传统方式实现接口 2.使用匿名内部类进行简化 一、匿名内部类介绍 1.匿名内部类也是类 2.匿名内部类也是内部类 3.匿名内部类没有名字&#xff08;名字存在于JVM中&#xff0c;不会显示出来&#xff09; 4.匿名内部类同时…

Pytorch深度学习笔记(八)处理多维度特征的输入

目录 1.概述 2. 处理多维度特征的输入 注&#xff1a;不同的激活函数&#xff08;sigmoid函数&#xff09;&#xff0c; 绘制不同的图表 推荐课程&#xff1a;07.处理多维特征的输入_哔哩哔哩_bilibili 1.概述 一个八维数据集&#xff1a; 数据集&#xff0c;每一行称为一个…

完整搭建 SpringCloud 项目

目录 1、数据库表结构设计 2、项目结构设计搭建 &#xff08;1&#xff09;创建父工程&#xff08;SpringBoot 类型&#xff09; &#xff08;2&#xff09;其他功能模块搭建 &#xff08;3&#xff09;创建 eureka &#xff08;4&#xff09;创建common 模块 &#xff08;5&…

jmeter常用组件1

常用的组件 常用的组件1&#xff0c;配置元件 1.HTTP请求默认值2.HTTP信息头管理器3.HTTP Cookie管理器4.用户自定义变量5.csv数据导入 2&#xff0c;集合点3&#xff0c;后置处理器 1.debug处理器2.json提取器 本文永久更新地址: 1&#xff0c;配置元件 1.HTTP请求默认值 在…

ASP.NET Core MVC 从入门到精通之Razor语法

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

PyQt在界面/子界面中添加横线

问题&#xff1a; 自己写个了处理数据的小程序&#xff0c;想要在不同的部分之间做个区域划分&#xff0c;使用实线或者虚线标记。 找了几个界面方法&#xff1a;1.使用画图QPainter,画所需要的线。 2.添加按钮&#xff0c;将按钮设置成线的形式 这两种方式都不适合我这个界…

着色器语言 GLSL (opengl-shader-language)入门大全

GLSL 中文手册 基本类型: 类型说明void空类型,即不返回任何值bool布尔类型 true,falseint带符号的整数 signed integerfloat带符号的浮点数 floating scalarvec2, vec3, vec4n维浮点数向量 n-component floating point vectorbvec2, bvec3, bvec4n维布尔向量 Boolean vectori…

广告投放ROI如何计算?实现广告效果最大化

大家好&#xff01;我是东哥&#xff0c;一个专注于跨境电商的小商家。今天&#xff0c;我要和大家分享一下广告投放中的一个关键指标——ROI&#xff0c;也就是投资回报率。这个指标非常重要&#xff0c;因为它可以帮助我们评估广告的效果&#xff0c;让我们知道我们的广告投放…

SpringCloud消息驱动——Stream

Stream 本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 SpringCloud Stream是SpringCloud的消息驱动&#xff0c;之前的微服务学的好好的&#xff0c;为什么会突然冒出一个这么个东西来增加我们的学习量呢&#xff1f; 一听到消息&#xff0c;那…

u盘文件不见但还占用容量文件办法?

将U盘插入电脑的时候为什么会出现“U盘文件突然不见但还占用空间”的提示框呢?遇到这个问题时又该怎么处理呢?别慌&#xff0c;下面小编就来给大家演示一下子解决U盘文件突然不见但还占用空间这个问题的解决方法。 u盘文件不见但还占用容量文件办法&#xff1f; u盘文件不见但…

短视频平台-小说推文(最右)推广任务详情

最右推荐书单 https://nr6mwfrzw8.feishu.cn/sheets/shtcnVgsBY18qft FqBG9b8eYFnc?sheetpfiUaC 复制链接到飞书或浏览器打开 最右会员 1.1关键词 最右关键词审核时间周一~周日 上午:10点前提交&#xff0c;15:00点前可查下午:15点前提交&#xff0c;20:00点前可查注意: …

盘点几款还不错的企业网盘产品

企业网盘的出现&#xff0c;为企业提供文件安全管理&#xff0c;团队协作服务&#xff0c;解决了便捷性与安全性等问题&#xff0c;受到了企业的青睐。市面上的企业网盘工具也是五花八门&#xff0c;我们该如何选择适合自己团队的网盘工具呢&#xff1f; 本文盘点了几款还不错的…

2023年软件测试的前景?测试工程师技能提升,进阶自动化测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 2023年软件测试行…