23种设计模式之C++实践(三)

2023-12-14 09:30:31

3. 设计模式

(三)行为型模式

14. 职责链模式——请求的链式处理

  1. 职责链模式(Chain of Responsibility Pattern):避免将请求接收者与发送者耦合在一起,让多个对象都有机会接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,知道有对象处理它为止。

  2. 要点

    1. 职责链模式并不创建职责链,职责链的创建工作通常是由客户端完成。
  3. 结构图

  4. 适用场景示例

    采购单的分级审批:不同级别的人员可以审批不同金额的采购单。

  5. 代码示例

    // Approver.h
    /**
     * @brief 审批者接口
     *
     */
    class Approver {
         
     protected:
      Approver* successor;
      std::string name;
    
     public:
      Approver(std::string name) : name(name) {
         }
    
      void setSuccessor(Approver* successor) {
         
        this->successor = successor;
        return;
      }
    
      virtual void processRequest(PurchaseRequest*) = 0;
    };
    
    class Director : public Approver {
         
     public:
      Director(std::string name) : Approver(name) {
         }
    
     public:
      void processRequest(PurchaseRequest* request) override {
         
        if (request->getAmount() < 5000) {
         
          printf("主任:%s 审批采购单:%d,金额:%f元,采购目的:%s。\n",
                 name.c_str(), request->getNumber(), request->getAmount(),
                 request->getPurpose().c_str());
          printf("审批通过!\n");
        } else {
         
          printf("%s审批通过,", name.c_str());
          printf("进入下一层审批\n");
          this->successor->processRequest(request);
        }
        return;
      }
    };
    
    class VicePresident : public Approver {
         
     public:
      VicePresident(std::string name) : Approver(name) {
         }
    
     public:
      void processRequest(PurchaseRequest* request) override {
         
        if (request->getAmount() < 10000) {
         
          printf("副董事长:%s 审批采购单:%d,金额:%f元,采购目的:%s。\n",
                 name.c_str(), request->getNumber(), request->getAmount(),
                 request->getPurpose().c_str());
          printf("审批通过!\n");
        } else {
         
          printf("%s审批通过,", name.c_str());
          printf("进入下一层审批\n");
          this->successor->processRequest(request);
        }
        return;
      }
    };
    
    class President : public Approver {
         
     public:
      President(std::string name) : Approver(name) {
         }
    
     public:
      void processRequest(PurchaseRequest* request) override {
         
        if (request->getAmount() < 200000) {
         
          printf("董事长:%s 审批采购单:%d,金额:%f元,采购目的:%s。\n",
                 name.c_str(), request->getNumber(), request->getAmount(),
                 request->getPurpose().c_str());
          printf("审批通过!\n");
        } else {
         
          printf("%s审批通过,", name.c_str());
          printf("进入下一层审批\n");
          this->successor->processRequest(request);
        }
        return;
      }
    };
    
    class Congress : public Approver {
         
     public:
      Congress(std::string name) : Approver(name) {
         }
    
     public:
      void processRequest(PurchaseRequest* request) override {
         
        if (request->getAmount() < 1000000) {
         
          printf("董事会:审批采购单:%d,金额:%f元,采购目的:%s。\n",
                 request->getNumber(), request->getAmount(),
                 request->getPurpose().c_str());
          printf("审批通过!\n");
        } else {
         
          printf("董事会审批不通过!\n");
        }
        return;
      }
    };
    
    // PurchaseRequest.h
    class PurchaseRequest {
         
     private:
      double amount;
      int number;
      std::string purpose;
    
     public:
      PurchaseRequest(double amount, int number, std::string purpose)
          : amount(amount), number(number), purpose(purpose) {
         }
    
      void setAmount(double amount) {
         
        this->amount = amount;
        return;
      }
      void setNumber(double number) {
         
        this->number = number;
        return;
      }
      void setPurpose(std::string purpose) {
         
        this->purpose = purpose;
        return;
      }
    
      double getAmount() {
          return this->amount; }
      int getNumber() {
          return this->number; }
      std::string getPurpose() {
          return this->purpose; }
    };
    
  6. 代码测试

    • 测试代码

      int main(int argc, char** argv) {
             
        printf("I'm Chain Of Responsibility Pattern!\n");
        // begin test
        Approver *num1, *num2, *num3, *num4;
        num1 = new Director("季");
        num2 = new VicePresident("叔");
        num3 = new President("仲");
        num4 = new Congress("伯");
      
        num1->setSuccessor(num2);
        num2->setSuccessor(num3);
        num3->setSuccessor(num4);
      
        PurchaseRequest* request = new PurchaseRequest(500, 10001, "差旅报销");
        num1->processRequest(request);
      
        PurchaseRequest* request2 = new PurchaseRequest(5000, 10002, "差旅报销");
        num1->processRequest(request2);
      
        PurchaseRequest* request3 = new PurchaseRequest(50000, 10003, "差旅报销");
        num1->processRequest(request3);
      
        PurchaseRequest* request4 = new PurchaseRequest(500000, 10004, "差旅报销");
        num1->processRequest(request4);
      
        PurchaseRequest* request5 = new PurchaseRequest(5000000, 10005, "差旅报销");
        num1->processRequest(request5);
      
        // end test
        return 0;
      }
      
      
    • 输出

      I’m Chain Of Responsibility Pattern!
      主任:季 审批采购单:10001,金额:500.000000元,采购目的:差旅报销。
      审批通过!
      季审批通过,进入下一层审批
      副董事长:叔 审批采购单:10002,金额:5000.000000元,采购目的:差旅报销。
      审批通过!
      季审批通过,进入下一层审批
      叔审批通过,进入下一层审批
      董事长:仲 审批采购单:10003,金额:50000.000000元,采购目的:差旅报销。
      审批通过!
      季审批通过,进入下一层审批
      叔审批通过,进入下一层审批
      仲审批通过,进入下一层审批
      董事会:审批采购单:10004,金额:500000.000000元,采购目的:差旅报销。
      审批通过!
      季审批通过,进入下一层审批
      叔审批通过,进入下一层审批
      仲审批通过,进入下一层审批
      董事会审批不通过!

职责链模式总结
  • 优点
    1. 使得一个对象无须知道其他哪一个对象处理其请求。
    2. 请求处理对象只需维持一个指向其后继者的引用,可简化对象的相互连接。
    3. 给对象分配职责时更灵活
    4. 在客户端增加一个新的具体请求处理者时无需修改原有代码,只需要在客户端重建链即可。
  • 缺点
    1. 请求可能因职责链没有被正确分配而得不到处理
    2. 职责链过长时,影响系统性能。
    3. 可能造成死循环
  • 适用场景
    1. 有多个对象处理同一个请求,具体哪个对象处理该请求待运行时再确定。
    2. 在不明确指定接收者的情况下,向多个对象种的一个提交一个请求。
    3. 可动态指定一组对象处理请求。

15. 命令模式:请求发送者与接收者解耦

  1. 命令模式(Command Pattern):将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

  2. 要点

    1. 引入抽象命令类,对请求进行封装,将发出命令的责任与执行命令的责任分割开。
  3. 结构图

  4. 适用场景示例

    自定义功能键

  5. 代码示例

    // Command.h
    /**
     * @brief 抽象命令类
     *
     */
    class Command {
         
     public:
      virtual void execute() = 0;
    };
    
    /**
     * @brief 帮助命令类:具体命令类
     *
     */
    class HelpCommand : public Command {
         
     private:
      HelpHandler* helpHandler;
    
     public:
      HelpCommand() {
          helpHandler = new HelpHandler(); }
    
     public:
      void execute() override {
         
        helpHandler->display();
        return;
      }
    };
    
    /**
     * @brief 最小化命令类:具体命令类
     *
     */
    class MinimizeCommand : public Command {
         
     private:
      WindowHandler* helpHandler;
    
     public:
      MinimizeCommand() {
          helpHandler = new WindowHandler(); }
    
     public:
      void execute() override {
         
        helpHandler->minimize();
        return;
      }
    };
    
    // FunctionButton.h
    /**
     * @brief 功能键类:请求发送者
     *
     */
    class FunctionButton {
         
     private:
      std::string name;
      Command* command;
    
     public:
      FunctionButton(std::string name) : name(name) {
         }
    
      std::string getName() {
          return name; }
    
      void setCommand(Command* command) {
         
        this->command = command;
        return;
      }
    
      void onClick() {
         
        printf("点击功能键\n");
        command->execute();
        return;
      }
    };
    
    class FBSettingWindow {
         
     private:
      std::string title;
      std::vector<FunctionButton*> functionButtons;
    
     public:
      FBSettingWindow(std::string title) {
          this->title = title; }
    
     public:
      void setTitle(std::string title) {
         
        this->title = title;
        return;
      }
    
      std::string getTitle() {
          return this->title; }
    
      void addFunctionButton(FunctionButton* fb) {
         
        functionButtons.push_back(fb);
        return;
      }
    
      void removeFunctionButton(FunctionButton* fb) {
         
        auto begin = functionButtons.begin();
        auto end = functionButtons.end();
        while (begin != end) {
         
          if (*begin == fb) {
         
            functionButtons.erase(begin);
            break;
          }
          begin++;
        }
        return;
      }
    
      void display() {
         
        printf("显示窗口:%s\n", this->title.c_str());
        printf("显示功能键:");
        for (auto fb : functionButtons) {
         
          printf("%s ", fb->getName().c_str());
        }
        printf("\n");
        return;
      }
    };
    
    // Handler.h
    /**
     * @brief 帮助文档处理类:请求接收者
     *
     */
    class HelpHandler {
         
     public:
      void display() {
         
        printf("显示帮助文档\n");
        return;
      }
    };
    
    /**
     * @brief 窗口处理类:请求接收者
     *
     */
    class WindowHandler {
         
     public:
      void minimize() {
         
        printf("将窗口最小化至托盘\n");
        return;
      }
    };
    
  6. 代码测试

    • 测试代码

      int main(int argc, char** argv) {
             
        printf("I'm Command Pattern!\n");
        // begin test
        FBSettingWindow* fbsw = new FBSettingWindow("功能键设置");
      
        FunctionButton *fb1, *fb2;
        fb1 = new FunctionButton("功能键1");
        fb2 = new FunctionButton("功能键2");
      
        Command *command1, *command2;
        command1 = new HelpCommand();
        command2 = new MinimizeCommand();
      
        fb1->setCommand(command1);
        fb2->setCommand(command2);
      
        fbsw->addFunctionButton(fb1);
        fbsw->addFunctionButton(fb2);
        fbsw->display();
      
        fb1->onClick();
        fb2->onClick();
      
        // end test
        return 0;
      }
      
      
    • 输出

      I’m Command Pattern!
      显示窗口:功能键设置
      显示功能键:功能键1 功能键2
      点击功能键
      显示帮助文档
      点击功能键
      将窗口最小化至托盘

命令模式总结
  • 优点
    1. 降低系统的耦合度。
    2. 增加新的命令很容易
    3. 可以比较容易地设计一个命令队列。
    4. 可使用命令队列实现”撤销请求“和”回复请求“。
  • 缺点
    1. 可能导致某些系统中有过多的具体命令类。
  • 适用场景
    1. 系统需要将请求调用者与请求接收者解耦。
    2. 系统需要在不同的时间指定请求、将请求排队和执行请求。
    3. 系统需要支持命令的撤销操作和恢复操作。

16. 解释器模式——自定义语言的实现

  1. 解释器模式(Interpreter Pattern

文章来源:https://blog.csdn.net/weixin_50749380/article/details/134762039
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。