设计模式(8)——命令模式(Command Pattern,行为型)

1.概述

使用设计模式可以提高代码的可复用性、可扩充性和可维护性。命令模式(Command Pattern)属行为型,将请求封装成对象,以便使用不同的请求、请求日志或请求队列等来参数化其他对象。命令模式也支持撤销操作。

通俗来讲,通过增加一个命令对象,放在请求者与请求的接收者之间,来达到二者的解耦合。一个请求对应一个命令对象,命令对象将请求的接收者和完成请求的基本操作封装在一起,对外提供execute()方法。完成请求的基本操作由接收者提供,在execute()方法中被调来完成请求,请求者如果调用execute(),请求的目的就能达到,而不需要直接与请求的接收者交互,降低了耦合度。

类图结构如下:
这里写图片描述

Client类:最终的客户端调用类。
Invoker类:调用者,调用具体命令类提供的execute()方法来完成请求。
Command类:是一个抽象类,一般需要对外公布一个execute()方法来完成请求。
ConcreteCommand类:Command类的实现类,对抽象类中声明的execute()方法进行实现。
Receiver类:接收者,动作的执行者,对外提供基本操作,交由ConcreteCommand类调用,来完成Invoker类的请求。

2.命令模式简单实现

举一个生活的例子,去餐厅吃饭的流程一般是这样的,服务员叫消费者点餐,下单生成订单,订单送往厨房交由厨师烹饪。四个角色,消费者对应Client类,服务员对应Invoker类,订单对应ConcreteCommand类,厨师对应Receiver类。服务员通过订单,无需自己完成食物的烹饪,而是交由厨师来完成,所以服务员是烹饪食物该动作的请求者,订单充当命令的角色,厨师是完成食物烹饪的动作执行者。所以上面的点餐过程可以通过命令模式来实现。

动作执行者厨师类:

class Chef {
public:
    void cook() const{
        cout << "开始烹饪"<< endl;
    }
};

充当命令角色的订单类:

//抽象命令类
class Command{
public:
    virtual void execute() const = 0;
};

//订单类
class OrderCommand:public Command{
private:
    const Chef& chef;
public:
    OrderCommand(const Chef& chef):chef(chef){}
    virtual void execute() const{
        chef.cook();
    }
};

烹饪动作请求者服务员类:

class Attendant {
private:
    const OrderCommand& orderCommand;
public:
    Attendant(const OrderCommand& orderCommand):orderCommand(orderCommand){}
    void cook() {
        orderCommand.execute();
    }
};

客户端代码,消费者进入餐厅后开始点餐:

int main() {
    //开始下单
    Chef chef;
    OrderCommand orderCommand(chef);
    Attendant attendant(orderCommand);
    //请求烹饪
    attendant.cook();
}

运行结果:

开始烹饪

3.命令模式的应用场景和优缺点

应用场景:
对于大多数请求-响应模式的功能,比较适合使用命令模式,正如命令模式定义说的那样,命令模式对实现记录日志、撤销操作等功能比较方便。

优点:
(1)命令模式有效地将发出请求的对象和执行请求的对象解耦。
(2)命令模式的封装性好。每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。
(3)命令模式的扩展性好。在命令模式中,接收者类对外提供基本操作,命令类对这些基本的操作进行封装,当增加新命令的时候,包装所需的接收者类对外提供的操作即可,代码的复用性好。比如,文件操作中,我们需要增加一个剪切命令,则只需要把复制文件和删除文件这两个命令组合一下就行了,非常方便。

缺点:
如果请求很多,使用命令模式,每一条请求都需要封装成一个命令类,会造成类数量膨胀。

4.小结

(1)命令模式属行为型,将请求封装成对象,以便使用不同的请求、请求日志或请求队列等来参数化其他对象。命令模式也支持撤销操作。
(2)请求日志指的是将所有的请求记录在日志中,一旦系统死机,我们可以对请求(命令对象)重新加载,成批地依次调用这些命令对象的execute()方法。
(3)请求队列指将请求形成队列,依次从队列中取出命令对象,调用它的execute()方法完成请求。

“使用不同的请求、请求日志或请求队列等来参数化其他对象”意味可以将命令对象、记录在日志文件中的命令对象列表和命令对象构成的队列当成参数来使用,传递给其他类对象。


参考文献

[1]Head First 设计模式(中文版):193-230

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值