设计模式

一、设计模式是什么

  • 设计模式是指在软件开发中,经过验证 的,用于解决在特定环境 下,重复出现 的,特定问题的解决方案。
  • 解决问题的固定套路。
  • 慎用设计模式。

二、设计模式是怎么来的

  • 满足设计原则 后,慢慢迭代出来的。

三、设计模式解决了什么问题

  • 前提:具体需求既有稳定点 ,又有变化点
  • 期望修改少量的代码,就可以适应需求的变化。
  • 比喻:整洁的房间,好动的猫,怎么保证房间的整洁 → 把猫关在一个笼子里。

四、设计模式基础

  • 面向对象的思想:
    • 封装:隐藏实现细节,实现模块化。
    • 继承:无需修改原有类的情况下通过继承实现对功能的扩展。
    • 多态:
      • 静态多态:函数重载。
      • 动态多态:继承中虚函数重写
  • 设计原则:
    • 依赖倒置:
      • 代码实现要依赖接口,接口又可以转化为抽象,也就是代码实现要依赖抽象,用户使用也需要依赖抽象。
      • 用户使用和代码实现的解耦,也就是接口的使用者不依赖具体的实现
    • 开闭原则:对扩展开放,对修改关闭。封装、多态。
    • 面向接口:封装。
    • 封装变化点:封装、多态。
    • 单一职责:封装。
    • 里氏替换:多态。
    • 接口隔离。
    • 组合优于继承。
    • 最小知道原则:封装。

五、设计模式学习步骤

  • 该设计模式解决了什么问题 → 稳定点变化点
  • 该设计模式的代码结构是什么。
  • 该设计模式符合哪些设计原则。
  • 如何在该设计模式上扩展代码。
  • 该设计模式有哪些典型应用场景。

六、模板方法

  • 解决的问题:
    • 稳定点:算法的骨架。
    • 变化点:子流程需要变化。
  • 符合哪些设计原则:
    • 单一职责:只暴露算法的骨架接口,其它的子流程接口都用 protectedprivate 封装起来。
    • 开闭原则:不能改变算法的骨架,只能修改子流程。
    • 依赖倒置。
      • 子类扩展时,需要依赖基类的虚函数实现。
      • 使用者只依赖接口。
    • 封装变化点:变化的地方通过 protected 暴露给子类去扩展。
    • 接口隔离:通过 protectedprivate 隔离接口。
    • 最小知道原则:对于用户而言,只需要知道算法的骨架接口即可。
  • 如何扩展:
    • 实现子类继承基类,复写子流程。
    • 通过多态调用方式使用。
  • 代码结构:
    • 基类中有骨架流程接口。

    • 所有子流程对子类开放并且是虚函数。

    • 通过多态调用方式使用。

      cpp 复制代码
      // 背景:某个动物园,有一套固定的表演流程,但是其中若干个表演子流程可替换,以迭代更新表演流程。
      /*
       *  接口隔离原则:1、类封装,使用权限限定词来实现。 2、类与类依赖,使用接口实现(依赖注入)
       *  最小知道原则
       *  单一职责原则:类封装
       *  开闭原则:对扩展开放,对修改关闭
       *  里氏替换原则:多态
       *
       *  扩展方式:继承、多态组合
       */
      
      using namespace std;
      
      class ZooShow {
      public:
          void Show() {
              if (Show0()) {
                  PlayGame();
              }
              Show1();
              Show2();
              Show3();
          }
      
      private:
          void PlayGame() {
              cout << "after Show0, then play game" << endl;
          }
      // 对其它用户关闭,但是对子类开放
      protected:
          bool expired;
          virtual bool Show0() {
              cout << "Show0" << endl;
              if (!expired) {
                  return true;
              }
              return false;
          }
          virtual void Show1() {
              cout << "Show1" << endl;
          }
          virtual void Show2() {
              cout << "Show2" << endl;
          }
          virtual void Show3() {
              cout << "Show3" << endl;
          }
      };
      
      // 模板方法
      class ZooShowEx1: public ZooShow {
      protected:
          virtual bool Show0()  {
              cout << "Show0 Ex1" << endl;
              if (!expired) { // 里氏替换原则
                  return true;
              }
              return false;
          }
      };
      
      class ZooShowEx2: public ZooShow {
      protected:
          virtual void Show2() {
              cout << "Show2 E2" << endl;
          }
      };
      
      class ZooShowEx3: public ZooShow {
      protected:
          virtual bool Show0() {
              cout << "Show0 Ex3" << endl;
              if (!expired) {
                  return true;
              }
              return false;
          }
          virtual void Show2() {
              cout << "Show2 Ex3" << endl;
          }
      };
      
      int main() {
          std::cout << "Template Method!" << std::endl;
      
          // 第一次迭代
          ZooShow * zs1 = new ZooShowEx1; // 晚绑定
          zs1->Show();
      
          // 第二次迭代
          ZooShow * zs2 = new ZooShowEx2;
          zs2->Show();
      
          // 第三次迭代
           ZooShow * zs3 = new ZooShowEx3;
           zs3->Show();
      
          return 0;
      }

七、观察者模式

  • 解决的问题:

    • 稳定点:"一" 对 "多" 的依赖关系,"一" 变化,"多" 跟着变化。
    • 变化点:"多" 增加、"多" 减少。
  • 符合哪些设计原则:

    • 面向接口编程。
      • 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
      • 用户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
      • 减少系统中各部分的依赖关系,从而实现高内聚、低耦合的类型设计方案。
    • 接口隔离:类与类的依赖建立在一个接口上。
      • 容器存储接口。
      • 依赖注入。
    • 封装变化点:通过 AttachDetach 接口来封装变化点。
  • 如何扩展:

    • 继承实现接口。
    • 调用 AttachDetach
  • 代码结构:

    cpp 复制代码
    // 背景:气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到三个不同的显示终端
    // "一" 指的是数据中心,"多" 指的是不同的显示终端
    
    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;
        }
    };
    class DisplayB: public IDisplay {
    public:
        virtual void Show(float temperature) {
            cout << "DisplayB show" << endl;
        }
    };
    
    class DisplayC: public IDisplay {
        virtual void Show(float temperature) {
            cout << "DisplayC show" << endl;
        }
    };
    
    class DataCenter {
    public:
    	// 依赖注入
        void Attach(IDisplay * ob) {
            obs.push_back(ob);
        }
        // 依赖注入
        void Detach(IDisplay * ob) {
            obs.remove(ob);
        }
    
        void notify() {
            float temperature = CalcTemperature();
            for (auto ob : obs) {
                ob->Show(temperature);
            }
        }
    
    private:
        float CalcTemperature() {
            // 获取计算得到的温度
            float temperature = 20.f;
            return temperature;
        }
        // 容器存储接口
        std::list<IDisplay*> obs;
    };
    
    int main() {
        // 单例模式
        DataCenter *dataCenter = new DataCenter;
    
        // 某个模块
        DisplayA *dA = new DisplayA;
        dataCenter->Attach(dA);
    
        DisplayB *dB = new DisplayB;
        dataCenter->Attach(dB);
    
        DisplayC *dC = new DisplayC;
        dataCenter->Attach(dC);
    
        dataCenter->notify();
    
        dataCenter->Detach(dB);
        dataCenter->notify();
    }

八、策略模式

  • 解决的问题:

    • 稳定点:用户程序与算法的调用关系。
    • 变化点:算法内容改变、新加算法。
  • 符合哪些设计原则:

    • 接口隔离:类与类的依赖建立在一个接口上。
      • 依赖注入。
    • 面向接口编程。
    • 开闭原则。
  • 代码结构:

    cpp 复制代码
    // 背景:商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格。
    // 稳定点:抽象去解决它
    // 变化点:扩展(继承、多态的组合)去解决它
    
    using namespace std;
    
    class Context {
    
    };
    
    class ProStrategy {
    public:
       virtual double CalcPro(const Context &ctx) = 0;
    };
    
    class Fes_Spring: public ProStrategy {
    public:
        virtual double CalcPro(const Context &ctx) {
            cout << "Fes_Spring 0.8" << endl;
            return 0.8;
        }
    };
    
    class Fes_Common: public ProStrategy {
    public:
        virtual double CalcPro(const Context &ctx) {
            cout << "Fes_Common 0.5" << endl;
            return 0.7;
        }
    };
    
    class Promotion {
    public:
        // 依赖注入
        Promotion(ProStrategy *s) {
            s_ = s;
        }
        ~Promotion() {}
    
        double CalcPromotion(const Context &ctx) {
            return s_->CalcPro(ctx);
        }
    
    private:
        ProStrategy *s_;
    };
    
    int main() {
        Context ctx;
        ProStrategy *fs = new Fes_Spring;
        Promotion *pm = new Promotion(fs);
        pm->CalcPromotion(ctx);
    
        ProStrategy * fm = new Fes_Common;
        Promotion *pm2 = new Promotion(fm);
        pm2->CalcPromotion(ctx);
    
        return 0;
    }

九、单例模式

  • 解决的问题:

    • 稳定点:一个类仅有一个实例,并提供一个该实例的全局访问点。
    • 变化点:有多个类都是单例,能不能复用代码。
  • 代码结构:

    • 私有的构造函数和析构函数。
    • 禁掉拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。
    • 静态类成员函数。
    • 静态私有成员变量。
  • 版本一。

    cpp 复制代码
    /*
     * 存在问题:
     * 1、程序退出时,指针 _instance 会被释放,但是它所指向的堆内存不会被释放,所以 _instance 的析构函数是不会被调用的
     *    如果该单例需要操作文件,操作完毕后要调用析构函数释放文件资源,由于析构函数不会被调用,就会产生 bug
     */
    
    class Singleton {
    public:
        static Singleton * GetInstance() {
            if (_instance == nullptr) {
                _instance = new Singleton();
            }
            return _instance;
        }
    
    private:
        Singleton() { cout << "Singleton" << endl; }
        ~Singleton() { cout << "~Singleton" << endl; }
    
        Singleton(const Singleton &) = delete;            // 拷贝构造
        Singleton& operator=(const Singleton &) = delete; // 拷贝赋值构造
        Singleton(Singleton &&) = delete;                 // 移动构造
        Singleton& operator=(Singleton &&) = delete;      // 移动赋值构造
    
        static Singleton * _instance; // 在静态全局区分配
    };
    
    Singleton* Singleton::_instance = nullptr; // 静态成员需要初始化
    
    int main() {
        Singleton * s = Singleton::GetInstance();
    
        return 0;
    }
  • 版本二。

    cpp 复制代码
    /*
     * 存在问题:
     * 1、不支持多线程
     * 2、atexit() 是线程安全的,但是 GetInstance() 不是线程安全的
     */
    
    class Singleton {
    public:
        static Singleton * GetInstance() {
            if (_instance == nullptr) {
                _instance = new Singleton(); // 多线程情况下,会产生资源竞争,可能会创建多个对象
                atexit(Destructor);          // 当程序退出时,调用 Destructor 函数
            }
            return _instance;
        }
    
    private:
        static void Destructor() {
            if (nullptr != _instance) {
                delete _instance;
                _instance = nullptr;
            }
        }
    
        Singleton() { cout << "Singleton" << endl; }
        ~Singleton() { cout << "~Singleton" << endl; }
        Singleton(const Singleton &) = delete; 
        Singleton& operator=(const Singleton &) = delete; 
        Singleton(Singleton &&) = delete; 
        Singleton& operator=(Singleton &&) = delete; 
    
        static Singleton * _instance;
    };
    
    Singleton* Singleton::_instance = nullptr; 
    
    int main() {
        Singleton * s = Singleton::GetInstance();
    
        return 0;
    }
  • 版本三:懒汉模式

    • 在对象需要被使用时才进行初始化,而不是在程序启动时,也就是延迟初始化,实例在第一次被使用时创建 → 减少程序启动时的负载和内存占用。

    • 通过双重检测 解决多线程问题。

      cpp 复制代码
      /*
       * 存在问题:
       * C++98 的语义是基于单线程的,使用的是标准外的一些库,比如 pthread_create 等。
       * C++11 在其基础上进行了封装,封装了线程操作,比如 mutex、atomic、内存栅栏,用来解决多线程问题
       *    在多核时代,会对程序进行一些优化:
       *      1. 编译器重排
       *      2. CPU 重排
       *    这样有可能会违反顺序一致性,导致一些问题
       *      1. 可见性问题
       *      2. 执行序问题
       *    C++11 提供了一些解决方法
       *      1. 同步原语(原子变量 + 内存栅栏)(无锁编程)
       *      2. 锁
       */
      
      class Singleton { // 懒汉模式
      public:
          static Singleton * GetInstance() {
              /*
               * 双重检测解决多线程问题:
               * 第一次访问这个接口的时候才会涉及到资源竞争,需要加锁(写操作)
               * 第二次访问这个接口的时候,不涉及资源竞争,不需要加锁(读操作)
               */
              if (_instance == nullptr) {
                  // RAII:在类的生命周期进行资源管理
                  std::lock_guard<std::mutex> lock(_mutex);
                  if (_instance == nullptr) {
                      _instance = new Singleton();
                      /*
                       * new 是一个操作符
                       * (1)分配内存
                       * (2)调用构造函数
                       * (3)返回指针
                       * 多线程环境下,CPU 会进行指令重排(reorder)
                       * 有可能先调用(1)、(3),再调用(2),导致要操作对象数据的时候,对象数据还没有初始化,产生异常
                      */
                      atexit(Destructor);
                  }
              }
              return _instance;
          }
      
      private:
          static void Destructor() {
              if (nullptr != _instance) {
                  delete _instance;
                  _instance = nullptr;
              }
          }
      
          Singleton() { cout << "Singleton" << endl; }
          ~Singleton() { cout << "~Singleton" << endl; }
          Singleton(const Singleton &) = delete; 
          Singleton& operator=(const Singleton &) = delete; 
          Singleton(Singleton &&) = delete; 
          Singleton& operator=(Singleton &&) = delete; 
      
          static Singleton * _instance;
          static std::mutex _mutex;
      };
      
      Singleton* Singleton::_instance = nullptr; 
      std::mutex Singleton::_mutex; // 互斥锁初始化
      
      int main() {
          Singleton * s = Singleton::GetInstance();
      
          return 0;
      }
  • 版本四:饿汉模式

    • 单例实例在类被加载到内存时就立即初始化,这意味着无论这个实例后续是否被用到,它都会被创建。

    • 因为实例在类加载时完成初始化,所以实例的创建是线程安全的。

      cpp 复制代码
      class Singleton { // 饿汉模式
      public:
          // 提供静态方法访问实例
          static Singleton& GetInstance() {
              return instance;
          }
      
      private:
          Singleton() { cout << "Singleton" << endl; }
          ~Singleton() { cout << "~Singleton" << endl; }
          Singleton(const Singleton &) = delete; 
          Singleton& operator=(const Singleton &) = delete; 
          Singleton(Singleton &&) = delete; 
          Singleton& operator=(Singleton &&) = delete; 
      
          static Singleton instance; // 静态成员变量,类加载时初始化
      };
      
      Singleton Singleton::instance;
      
      int main() {
          Singleton& s = Singleton::GetInstance();
      
          return 0;
      }
  • 版本五。

    cpp 复制代码
    /*
     * 原子变量解决问题:
     * 1、原子执行的问题:同时只有一个线程去执行这条语句
    
     * 内存栅栏解决问题:
     * 2、可见性问题:通过 store(修改数据让其它线程可见) 和 load(可以看见其它线程最新操作的数据)解决
     * 3、执行序问题:内存模型
     *              memory_order_acquire:下面的代码不能优化到上面去
     *              memory_order_release:上面的代码不能优化到下面来
     */
    
    class Singleton {
    public:
        static Singleton * GetInstance() {
            // 解决原子执行问题、可见性问题、执行序问题
            Singleton * tmp = _instance.load(std::memory_order_acquire);
    
            if (tmp == nullptr) {
                std::lock_guard<std::mutex> lock(_mutex);
                tmp = _instance.load(std::memory_order_acquire);
                if (tmp == nullptr) {
                    tmp = new Singleton;
    
                    _instance.store(tmp, std::memory_order_release);
    
                    atexit(Destructor);
                }
            }
            return tmp;
        }
    
        static Singleton * GetInstance2() {
            // 如果 Singleton 构造函数中又用到了原子变量,需要进行拆分
    
            // 解决原子执行问题、可见性问题(std::memory_order_relaxed => 内存结构是松散的结构,不限制优化,效率最高)
            Singleton * tmp = _instance.load(std::memory_order_relaxed);
            // 解决执行序问题
            std::atomic_thread_fence(std::memory_order_acquire); // 获取内存屏障
    
            if (tmp == nullptr) {
                std::lock_guard<std::mutex> lock(_mutex);
                tmp = _instance.load(std::memory_order_relaxed);
                if (tmp == nullptr) {
                    tmp = new Singleton;
    
                    std::atomic_thread_fence(std::memory_order_release); // 释放内存屏障
                    _instance.store(tmp, std::memory_order_relaxed);
    
                    atexit(Destructor);
                }
            }
            return tmp;
        }
    
    private:
        static void Destructor() {
            if (nullptr != _instance) {
                delete _instance;
                _instance = nullptr;
            }
        }
    
        Singleton() { cout << "Singleton" << endl; }
        ~Singleton() { cout << "~Singleton" << endl; }
        Singleton(const Singleton &) = delete;
        Singleton& operator=(const Singleton &) = delete; 
        Singleton(Singleton &&) = delete; 
        Singleton& operator=(Singleton &&) = delete; 
    
        static std::atomic<Singleton *> _instance;
        static std::mutex _mutex;
    };
    
    std::atomic<Singleton *> Singleton::_instance; 
    std::mutex Singleton::_mutex; 
    
    int main() {
        Singleton * s = Singleton::GetInstance();
    
        return 0;
    }
  • 版本六。

    cpp 复制代码
    /*
     * C++11 magic static 特性:如果变量在初始化的时候并发同时进入声明语句,并发线程将会阻塞等待初始化结束
     * 1、利用静态局部变量特性,延迟加载
     * 2、利用静态局部变量特性,系统自动回收内存,自动调用析构函数
     * 3、静态局部变量初始化时,没有 new 操作带来的 cpu 指令重排(reorder)
     * 4、C++11 静态局部变量初始化时,具备线程安全
     */
    
    class Singleton {
    public:
        static Singleton& GetInstance() {
            static Singleton instance;
            return instance;
        }
    
    private:
        Singleton() { cout << "Singleton" << endl; }
        ~Singleton() { cout << "~Singleton" << endl; }
        Singleton(const Singleton &) = delete; 
        Singleton& operator=(const Singleton &) = delete; 
        Singleton(Singleton &&) = delete; 
        Singleton& operator=(Singleton &&) = delete; 
    };
    
    int main() {
        Singleton& s = Singleton::GetInstance();
    
        return 0;
    }
  • 最终版本。

    cpp 复制代码
    template<typename T>
    class Singleton {
    public:
        static T& GetInstance() {
            // 这里要初始化 DesignPattern,需要调用 DesignPattern 的构造函数,同时会调用父类的构造函数(先调用)
            static T instance;
            return instance;
        }
    
    protected:
        virtual ~Singleton() {
            cout << "~Singleton" << endl;
        }
        // protected 修饰构造函数,才能让别人继承
        Singleton() {
            cout << "Singleton" << endl;
        }
    
    private:
        Singleton(const Singleton &) = delete; 
        Singleton& operator=(const Singleton &) = delete; 
        Singleton(Singleton &&) = delete; 
        Singleton& operator=(Singleton &&) = delete; 
    };
    
    class DesignPattern : public Singleton<DesignPattern> {
        friend class Singleton<DesignPattern>; // 让基类能够调用子类的构造函数
    
    private:
        DesignPattern() {
            cout << "DesignPattern" << endl;
        }
        ~DesignPattern() {
            cout << "~DesignPattern" << endl;
        }
    };
    
    int main() {
        DesignPattern &dp = DesignPattern::GetInstance();
    
        return 0;
    }

十、工厂模式

  • 定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使得一个类的实例化延迟到子类。
  • 为什么要有工厂模式,而不直接使用 new ?
    • 因为除了 new,还有复杂的构造流程。
    • 工厂模式是为了解决过程比较复杂,希望对外隐藏这些细节的场景。
      • 比如连接池、线程池。
      • 隐藏对象真实类型。
      • 创建对象有复杂的依赖关系。
  • 解决的问题:
    • 稳定点:创建同类对象的接口(对象创建接口)、同类对象有一个相同的职责(功能接口)。
    • 变化点:创建对象的扩展。
  • 符合哪些设计原则:
    • 最小知道原则。
    • 面向接口编程。
  • 如何扩展:
    • 实现对象创建接口。
    • 实现功能接口。
    • 多态调用。
  • 代码结构:
    • 对象创建接口 → 创建具体对象,调用一个功能接口

    • 实现一个功能接口

      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) {
              cout << data << endl;
              return true;
          }
      };
      class ExportJson: public IExport {
      public:
          virtual bool Export(const std::string &data) {
              cout << data << endl;
              return true;
          }
      };
      class ExportTxt : public IExport {
      public:
          virtual bool Export(const std::string &data){
              cout << data << endl;
              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:
          virtual IExport * NewExport(/* ... */) {
              IExport * temp = new ExportTxt();
              return temp;
          }
      };
      
      int main() {
          IExportFactory * factory = new ExportTxtFactory();
          factory->Export("export txt");
      
          return 0;
      }

十一、抽象工厂

  • 定义:提供一个接口,让该接口负责创建一些列 "相关或者相互依赖的对象",无需指定它们具体的类。
  • 解决的问题:
    • 稳定点:创建同类对象的接口,同类对象有多个相同的职责。
    • 变化点:创建对象的扩展。
  • 工厂模式和抽象工厂的区别
    • 工厂模式一个对象只有一个职责,而抽象工厂一个对象有多个职责
  • 代码结构:
    • 对象创建接口 → 创建具体对象,调用多个功能接口

    • 实现多个功能接口

      cpp 复制代码
      // 实现一个导出导入数据的接口,让客户选择数据的导出导入方式。 
      
      class IObj {
      public:
          virtual bool Export(const std::string &data) = 0;
          virtual bool Import(const std::string &data) = 0;
          virtual ~IObj(){}
      };
      
      class XmlObj : public IObj {
      public:
          virtual bool Export(const std::string &data) {
              cout << data << endl;
              return true;
          }
          virtual bool Import(const std::string &data) {
              cout << data << endl;
              return true;
          }
      };
      
      class JsonObj: public IObj {
      public:
          virtual bool Export(const std::string &data) {
              cout << data << endl;
              return true;
          }
          virtual bool Import(const std::string &data) {
              cout << data << endl;
              return true;
          }
      };
      
      class TxtObj : public IObj {
      public:
          virtual bool Export(const std::string &data){
              cout << data << endl;
              return true;
          }
          virtual bool Import(const std::string &data) {
              cout << data << endl;
              return true;
          }
      };
      
      class IDataApiFactory {
      public:
          IDataApiFactory() {
              _obj = nullptr;
          }
          virtual ~IDataApiFactory() {
              if(_obj) {
                  delete _obj;
                  _obj = nullptr;
              }
          }
          bool Export(const std::string &data) {
              if (_obj == nullptr) {
                  _obj = NewObj();
              }
              return _obj->Export(data);
          }
          bool Import(const std::string &data) {
              if (_obj == nullptr) {
                  _obj = NewObj();  
              }
              return _obj->Import(data);
          }
      protected:
          virtual IObj * NewObj(/* ... */) = 0;
      private:
          IObj* _obj;
      };
      
      class XmlApiFactory : public IDataApiFactory {
      protected:
          virtual IObj * NewObj(/* ... */) {
              IObj * temp = new XmlObj();
              return temp;
          }
      };
      
      class JsonApiFactory : public IDataApiFactory {
      protected:
          virtual IObj * NewObj(/* ... */) {
              IObj * temp = new JsonObj();
              return temp;
          }
      };
      
      class TxtApiFactory : public IDataApiFactory {
      protected:
          virtual IObj * NewObj(/* ... */) {
              IObj * temp = new TxtObj();
              return temp;
          }
      };
      
      int main() {
          IDataApiFactory * jsonObj = new JsonApiFactory();
          jsonObj->Import("import json");
          jsonObj->Export("export json");    
      
          IDataApiFactory * txtObj = new TxtApiFactory();
          txtObj->Import("import txt");
          txtObj->Export("export txt");
      
          return 0;
      }

十二、责任链

  • 定义:使多个节点都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些节点连成一条链,并沿着这条链传递请求,直到有一个节点处理它为止。
  • 解决的问题:
    • 稳定点:处理流程 → 请求按照链条进行传递(链表关系类型是接口),只要有一个节点处理了,就会返回,
    • 变化点:处理节点的个数、处理顺序、处理条件。
  • 扩展代码:
    • 实现处理接口。
    • 修改静态接口。
  • 符合哪些设计原则:
    • 组合优于继承。
    • 面向接口编程。
    • 接口依赖。
  • 代码结构:
    • 从单个节点出发,实现处理接口,实现一个构建链表关系的静态接口。

      cpp 复制代码
      // 背景:请假流程,1 天内需要组长批准,3 天内需要项目经理批准,3 天以上需要老板批准
      
      class Context {
      public:
          std::string name;
          int day;
      };
      
      // 从单个处理节点出发,我能处理,我处理,我不能处理,交给下一个人处理
      // 链表关系如何抽象
      class IHandler {
      public:
          IHandler(): _next(nullptr) {}
          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 HandleByLeader: public IHandler {
      protected:
          virtual bool HandleRequest(const Context &ctx){
              return true;
          }
          virtual bool CanHandle(const Context &ctx) {
              if (ctx.day <= 1){
                  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 <= 3){
                  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 Request {
      public:
          Request() {
              IHandler * h0 = new HandleByLeader();
              IHandler * h1 = new HandleByProjMgr();
              IHandler * h2 = new HandleByBoss();
              h0->SetNextHandler(h1);
              h1->SetNextHandler(h2);
              handler = h0;
          }
          bool Handle(const Context& ctx) {
              return handler->Handle(ctx);
          }
      
      private:
          IHandler *handler;
      };
      
      int main() {
          Context ctx;
          ctx.day = 31;
          auto req = std::make_unique<Request>();
          bool state = req->Handle(ctx);
          if (state) {
              cout << "success" << endl;
          } else {
              cout << "failure" << endl;
          }
      
          return 0;
      }

十三、装饰器

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

  • 解决的问题:

    • 稳定点:顺序无关地增加职责。
    • 变化点:增加职责。
  • 符合哪些设计原则:

    • 组合优于继承。
    • 面向接口编程。
    • 接口依赖。
  • 代码结构:

    cpp 复制代码
    // 背景:普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金,后面可能会增加其他奖金,同时可能针对不同的职位产生不同的奖金组合。
    
    class Context {
    public:
        bool isMgr;
    };
    
    class CalcBonus {
    public:
        CalcBonus(CalcBonus * c = nullptr): cc(c) {
            if (cc != nullptr) {
                cout << "CalcBonus" << endl;
                _salary = cc->_salary;
            }
        }
        virtual double Calc(Context &ctx) {
            return _salary;
        }
        virtual ~CalcBonus() {}
    
    protected:
        CalcBonus* cc;
        double _salary = 10.0;
    };
    
    class CalcMonthBonus : public CalcBonus {
    public:
        CalcMonthBonus(CalcBonus *c) : CalcBonus(c) {
            cout << "CalcMonthBonus" << endl;
        };
        virtual double Calc(Context & ctx) {
            double mbonus = 100.0; // 忽略计算过程
            return mbonus + cc->Calc(ctx);
        }
    };
    
    class CalcSumBonus : public CalcBonus {
    public:
        CalcSumBonus(CalcBonus *c) : CalcBonus(c) {
            cout << "CalcSumBonus" << endl;
        };
        virtual double Calc(Context & ctx) {
            double sbonus = 200.0; // 忽略计算过程
            return sbonus + cc->Calc(ctx);
        }
    };
    
    class CalcGroupBonus : public CalcBonus {
    public:
        CalcGroupBonus(CalcBonus * c) : CalcBonus(c) {
            cout << "CalcGroupBonus" << endl;
        };
        virtual double Calc(Context &ctx) {
            double gbonus = 300.0; // 忽略计算过程
            return gbonus + cc->Calc(ctx);
        }
    };
    int main() {
        // 普通员工
        Context ctx_con;
    
        CalcBonus *base = new CalcBonus();
        CalcBonus *cb1= new CalcMonthBonus(base);
        CalcBonus *cb2 = new CalcSumBonus(cb1);
        double con_salary = cb2->Calc(ctx_con);
        cout << con_salary << endl;
    
        // 部门经理
        Context ctx_mgr;
    
        CalcBonus *cb3 = new CalcGroupBonus(cb2);
        double mgr_salary = cb3->Calc(ctx_mgr);
        cout << mgr_salary << endl;
    
        return 0;
    }

十四、组合模式

  • 解决的问题:

    • 稳定点:层次关系是稳定的,对象和组合对象的使用是一致的。
    • 变化点:对象的职责变更,组合对象里对象数量变更。
  • 代码结构:

    cpp 复制代码
    class IComponent {
    public:
        IComponent() {};
        ~IComponent() {};
        virtual void Execute() = 0;
        virtual void AddChild(IComponent *ele){}
        virtual void RemoveChild(IComponent *ele){}
    };
    
    class Leaf : public IComponent {
    public:
        virtual void Execute (){
            cout << "left execute" << endl;
        }
    };
    
    class Composite : public IComponent {
    private:
        std::list<IComponent*> _list;
    public:
        virtual void AddChild(IComponent *ele) {
            // ...
        }
        virtual void RemoveChild(IComponent *ele){
            // ...
        }
        virtual void Execute() {
            for (auto iter = _list.begin();iter != _list.end();iter++){
                (*iter)->Execute();
            }
        }
    };

相关推荐
WaaTong14 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
霁月风17 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
暗黑起源喵3 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
wrx繁星点点10 小时前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
金池尽干12 小时前
设计模式之——观察者模式
观察者模式·设计模式
也无晴也无风雨12 小时前
代码中的设计模式-策略模式
设计模式·bash·策略模式
捕鲸叉21 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
wrx繁星点点1 天前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
凉辰1 天前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
菜菜-plus1 天前
java设计模式之策略模式
java·设计模式·策略模式