首页 > 编程语言 >【重走编程路】设计模式概述(十) -- 责任链模式、命令模式

【重走编程路】设计模式概述(十) -- 责任链模式、命令模式

时间:2024-07-15 08:58:48浏览次数:16  
标签:std 请求 处理 -- 接收者 模式 命令 设计模式

文章目录


前言

行为型模式关注对象之间的交互以及如何分配职责,提供了一种定义对象之间的行为和职责的最佳方式。本章介绍创建型模式中的责任链模式和命令模式。


17. 责任链模式(Chain of Responsibility)

定义

责任链模式为请求的发送者和接收者之间解耦提供了一种松散的耦合方式,使得多个对象都有机会处理这个请求,或者将这个请求传递给链中的下一个对象,直到有一个对象处理它为止。换句话说,这种模式创建了一个接收者对象的链,并沿着这条链传递请求,直到有一个对象处理它为止。

问题

在软件设计中,有时会遇到一个请求可能由多个对象中的任何一个处理,但具体由哪个对象处理在运行时才能确定。如果将请求的发送者与接收者直接绑定,会导致系统难以扩展和维护。

解决方案

责任链模式通过将请求的发送者与接收者解耦,使得请求的发送者不需要知道哪个接收者会处理该请求,同时也使得新的接收者可以很容易地加入到系统中。责任链模式通过以下几个步骤来解决问题:

  1. 定义抽象处理者(Handler)角色:创建一个抽象的类,用于定义处理请求的接口,包含一个指向下一个处理者的引用(通常是同类型的另一个对象)和一个处理请求的方法。
  2. 实现具体处理者(Concrete Handler)角色:实现抽象处理者定义的接口,具体处理它负责的请求,也可以将请求传递给链中的下一个处理者,或者终止请求链。
  3. 组装链:在程序的运行时刻,按照需要将处理者对象组合成链,并明确请求的传递方向。
#include <iostream>  
#include <string>  
  
// 抽象处理者  
class LogHandler {  
protected:  
    LogHandler* nextHandler;  
  
public:  
    void SetNextHandler(LogHandler* nextHandler) {  
        this->nextHandler = nextHandler;  
    }  
  
    virtual void Handle(const std::string& logLevel, const std::string& message) = 0;  
};  
  
// 具体处理者:控制台日志处理器  
class ConsoleLogHandler : public LogHandler {  
public:  
    void Handle(const std::string& logLevel, const std::string& message) override {  
        if (logLevel == "DEBUG" || logLevel == "INFO") {  
            std::cout << "Console: " << message << std::endl;  
        }  
        if (nextHandler != nullptr) {  
            nextHandler->Handle(logLevel, message);  
        }  
    }  
};  
  
// 具体处理者:文件日志处理器  
class FileLogHandler : public LogHandler {  
public:  
    void Handle(const std::string& logLevel, const std::string& message) override {  
        if (logLevel == "INFO" || logLevel == "ERROR") {  
            // 假设有文件写入逻辑  
            std::cout << "File: " << message << std::endl; // 模拟写入文件  
        }  
        if (nextHandler != nullptr) {  
            nextHandler->Handle(logLevel, message);  
        }  
    }  
};  
  
// 客户端代码  
int main() {  
    LogHandler* consoleHandler = new ConsoleLogHandler();  
    LogHandler* fileHandler = new FileLogHandler();  
    consoleHandler->SetNextHandler(fileHandler);  
  
    // 发送日志请求  
    consoleHandler->Handle("DEBUG", "This is a debug message.");  
    consoleHandler->Handle("INFO", "This is an info message.");  
    consoleHandler->Handle("ERROR", "This is an error message.");  
    // 注意:在实际应用中,最好使用智能指针管理动态分配的内存
    delete consoleHandler;
    delete fileHandler;
    return 0;  
}

应用场景

  1. 多个对象可以处理同一请求,但具体由哪个对象处理在运行时动态决定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态地增加或删除处理者。

优缺点

优点:

  • 降低耦合度:请求者与接收者之间解耦,提高了系统的灵活性和可扩展性。
  • 简化对象间的连接:对象之间不需要显式地指定请求的接收者,只需要将请求发送到链上即可。
  • 增强系统的动态性:可以在运行时动态地添加或删除处理者。

缺点:

  • 可能导致请求无法处理:如果没有合适的处理者能够处理请求,那么请求可能会在链中一直传递直到链的末尾而得不到处理。
  • 性能问题:请求在链中传递时,需要经过多个处理者的判断和处理,这可能会增加系统的响应时间。特别是当链很长或者每个处理者的处理逻辑较为复杂时,性能问题会更加明显。
  • 调试难度增加:由于请求可能在多个处理者之间传递,因此当出现问题时,定位问题可能会比较困难。

18. 命令模式(Command)

定义

命令模式将一个请求封装为一个对象,从而使你可用不同的请求、队列、日志来参数化其他对象。命令模式也支持可撤销的操作。在命令模式中,命令的发送者和接收者之间是完全解耦的,发送者通过命令对象来间接地调用接收者的相关方法。

问题

在软件设计中,有时需要将请求的发送者和接收者解耦,以便在不改变发送者代码的情况下增加新的接收者,或者在不改变接收者代码的情况下增加新的命令。此外,还需要支持命令的撤销、重做等复杂操作。命令模式通过封装请求为对象的方式,提供了一种灵活的方式来处理这些需求。

解决方案

命令模式通过以下几个步骤来解决问题:

  1. 定义命令接口(Command):创建一个接口,用于声明执行操作的方法。
  2. 具体命令类(Concrete Command):实现命令接口,并关联一个接收者对象,通过调用接收者的方法来执行请求。
  3. 调用者(Invoker):负责调用命令对象的执行方法,它通常持有命令对象的引用。
  4. 接收者(Receiver):知道如何执行命令要求的操作,接收者会执行命令中要求执行的操作。
#include <iostream>  
#include <memory>  
#include <vector>  
  
// 接收者接口  
class Receiver {  
public:  
    virtual void Action() = 0;  
    virtual ~Receiver() {}  
};  
  
// 具体接收者:电视  
class TV : public Receiver {  
public:  
    void Action() override {  
        std::cout << "TV is on." << std::endl;  
    }  
};  
  
// 命令接口  
class Command {  
protected:  
    std::shared_ptr<Receiver> receiver;  
  
public:  
    Command(std::shared_ptr<Receiver> receiver) : receiver(receiver) {}  
    virtual void Execute() = 0;  
    virtual ~Command() {}  
};  
  
// 具体命令:打开电视  
class TVOnCommand : public Command {  
public:  
    TVOnCommand(std::shared_ptr<Receiver> receiver) : Command(receiver) {}  
    void Execute() override {  
        receiver->Action();  
    }  
};  
  
// 调用者:遥控器  
class RemoteControl {  
private:  
    std::vector<std::shared_ptr<Command>> commands;  
  
public:  
    void SetCommand(int slot, std::shared_ptr<Command> command) {  
        if (slot >= 0 && slot < commands.size()) {  
            commands[slot] = command;  
        }  
    }  
  
    void PressButton(int slot) {  
        if (slot >= 0 && slot < commands.size() && commands[slot] != nullptr) {  
            commands[slot]->Execute();  
        }  
    }  
  
    void AddCommand(std::shared_ptr<Command> command) {  
        commands.push_back(command);  
    }  
};  
  
// 客户端代码  
int main() {  
    auto tv = std::make_shared<TV>();  
    auto tvOn = std::make_shared<TVOnCommand>(tv);  
  
    RemoteControl remote;  
    remote.AddCommand(tvOn);  
    remote.AddCommand(nullptr); // 假设第二个槽位不使用  
    remote.PressButton(0); // 输出: TV is on.  
    // remote.PressButton(1); // 如果需要,可以添加更多命令并调用  
    return 0;  
}

应用场景

  1. 需要抽象出待执行的动作,并指定其接收者。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交请求。
  3. 支持命令的撤销和重做。
  4. 需要保持请求的发送者和接收者之间的解耦。

优缺点

优点:

  • 降低耦合度:命令模式将请求的发送者和接收者解耦,提高了系统的灵活性和可扩展性。
  • 新的命令容易加入:如果需要增加新的命令,只需实现新的命令类即可,无需修改现有代码。
  • 支持撤销和重做:通过扩展命令模式,可以容易地实现命令的撤销和重做功能。

缺点:

  • 类爆炸:在命令模式中,每个具体的命令类都可能对应一个接收者类中的方法。如果系统中有很多这样的方法,那么就需要创建大量的具体命令类,这可能会导致系统中类的数量激增,增加系统的复杂性。
  • 系统复杂度增加: 命令模式增加了系统的抽象层次,使得系统的设计和实现变得更加复杂。对于简单的系统来说,使用命令模式可能会带来不必要的开销和复杂性。
  • 性能问题:由于命令模式涉及到多个类的实例化和方法调用,因此可能会带来一定的性能开销。

To be continued.

标签:std,请求,处理,--,接收者,模式,命令,设计模式
From: https://blog.csdn.net/weixin_44595767/article/details/140359884

相关文章

  • 每日一问,请你谈一谈你对HashMap的理解。
    HashMap底层是数组加链表的结构,在jdk1.8之后又加入了红黑树。当添加一个元素(key-value)时,首先计算键值对的key的hash值,以此来确定插入到数组中的位置;允许有null值和null键。如果根据hash值确定的数组位置中已经存在元素,就添加到同一个hash值的元素的后面,于是形成了链表;Entry也就......
  • 什么是枚举类?
    特点:用Enum关键字定义类枚举默认继承了java.long.Enum而不是Object枚举的默认构造方法只能用private修饰符,如果构造方法省略修饰符,则默认使用privatte修饰符.枚举所有的对象(实例)必须在枚举中显示列出,否则这个枚举将永远不能创建实例对象.枚举列出的对象,系统......
  • Java计算机毕业设计动物园售票系统的设计和实现(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加快和居民生活水平的提升,动物园作为集科普教育、休闲娱乐于一体的公共场所,日益受到公众的青睐。然而,传统的动物园售票方式往往存在......
  • Java计算机毕业设计停车场管理系统(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加速和汽车保有量的持续增长,停车难问题日益凸显,成为制约城市发展的瓶颈之一。传统停车场管理方式效率低下,信息不透明,车主常面临找车......
  • Python常用数据类型 新手必看 超详细介绍
    目录一、Int整型二、Float浮点型科学计数法三、Bool布尔类型bool函数四、Str字符型字符串的声明字符串的常见操作查找:计数:大小写转换:编码与解码:切割与拼接:替换:五、None六、List列表列表的声明列表的常见操作 增加元素:删除元素:其他:七、Tuple元组元组的......
  • pthon中pyglet框架的使用方法
    pyglet框架的使用方法可以分为以下几个步骤:一、安装pyglet首先,需要通过pip安装pyglet。在命令行中输入以下命令:pipinstallpyglet确保Python环境配置正确,以便顺利安装pyglet库。二、创建窗口使用pyglet创建游戏窗口是游戏开发的第一步。以下是一个简单的示例代码,展......
  • pygame.display功能的使用方法
    pygame.display是Pygame库中的一个模块,它主要负责与游戏窗口的显示相关的功能。以下是对pygame.display功能的详细使用方法,按照清晰和有条理的格式进行归纳:1.初始化在使用pygame.display之前,需要先初始化Pygame。这通过pygame.init()完成,它会初始化所有Pygame模块,包括dis......
  • Python网页开发的常用框架
    Python网页开发的框架众多,各有其独特的特点、缺点以及在性能上的优劣势。以下是一些主流的Python网页开发框架及其特点的详细介绍:1.Django特点:全功能框架:Django是一个高级PythonWeb框架,鼓励快速开发和干净、实用的设计。它遵循MVC(模型-视图-控制器)设计模式,但Django中更......
  • FL Studio 24.1.1.4234官方中文破解安装使用指南
    FLStudio24.1.1.4234破解版是很多音乐人都在用的全功能的音乐工作站,里面拥有非常先进的制作工具,音符编辑器,音效编辑器,便捷的音源输入,让用户可以自由的在这里自由的改编歌曲,打造出你想要的曲风效果,操作方便,推荐给玩音乐的朋友!这是很多音乐人和音乐爱好者喜欢和追棒的虚拟音......
  • FL Studio 24.1.1官方中文破解版全新发布
    flstudio2024全称FruityLoopsStudio2024,这款软件也被人们亲切的称之为水果,它是一款功能强大的音乐创作编辑软件,拥有全功能的录音室,大混音盘以及先进的音乐制作工具,用户通过使用该软件,就可以轻松制作出自己的音乐唱片是非常好用的一款音乐创作编辑软件。FLStudio24.1.1官......