likes
comments
collection
share

深究设计模式之-命令模式

作者站长头像
站长
· 阅读数 3

命令模式(Command Pattern) 是一种行为型设计模式,其主要目的是将请求发送者和请求接收者解耦,将一个请求封装成一个对象,从而可以灵活地参数化、队列化和日志化请求。命令模式使得请求发送者不需要知道请求的具体操作和接收者,只需知道如何发送请求即可。

举一个例子来说明命令模式:餐厅点餐服务

想象我们在一家繁忙的餐厅用餐,服务员作为命令模式的实现者,接收到顾客的点餐请求并将其传递给厨房。在这里,顾客就是命令的发起者,服务员是命令的调用者,而厨房则是命令的接收者和执行者。 在这个场景中,顾客并不直接与厨房沟通,而是通过服务员来传达他们的点餐需求。服务员将顾客的点餐信息封装成一个命令,并将其传递给厨房。这样,服务员和厨房之间解耦,服务员不需要知道具体如何制作每道菜,只需知道如何传递命令。

命令模式在这里带来了一些好处,比如增加了灵活性,因为服务员可以随时调整传递给厨房的命令,而不需要直接与厨房的具体实现细节打交道。同时,也提高了可扩展性,如果餐厅要添加新的菜品或改变制作方式,只需要修改厨房的实现,而不影响顾客点餐的流程。

主要角色:

  1. 命令接口(Command): 定义了执行请求的接口。
  2. 具体命令(Concrete Command): 实现了命令接口,包含了执行具体请求的操作。
  3. 请求发送者(Client): 创建并配置具体命令对象,并将其关联到请求接收者。
  4. 请求接收者(Receiver): 知道如何执行具体请求的操作。
  5. 调用者/请求者(Invoker): 负责调用命令对象执行请求。

示例:

这里实现一下上面这个餐厅的例子:

package com.luke.designpatterns.commandPattern;
//Command接口
interface OrderCommand {
    void execute();
}

//ConcreteCommand类
class PlaceOrderCommand implements OrderCommand {
    private Kitchen kitchen; // 接收者
    private String order; // 订单细节

    public PlaceOrderCommand(Kitchen kitchen, String order) {
        this.kitchen = kitchen;
        this.order = order;
    }

    @Override
    public void execute() {
        kitchen.prepareOrder(order);
    }
}

//Receiver类
//接收者类(厨房)负责具体执行命令:

class Kitchen {
    public void prepareOrder(String order) {
        System.out.println("厨房收到下单: " + order);
    }
}

//Invoker类
//服务员类充当调用者,负责接收命令并执行:
class Waiter {
    private OrderCommand command;

    public void takeOrder(OrderCommand command) {
        this.command = command;
    }

    public void placeOrder() {
        System.out.println("服务员接收到订单");
        command.execute();
    }
}

//Client类
//客户端类负责创建命令对象并设置其接收者:
public class Restaurant {
    public static void main(String[] args) {
        Kitchen kitchen = new Kitchen();
        OrderCommand command = new PlaceOrderCommand(kitchen, "羊肉泡");
        Waiter waiter = new Waiter();

        waiter.takeOrder(command);
        waiter.placeOrder();
    }
}

深究设计模式之-命令模式

在这个例子中

  • Command接口(OrderCommand): 定义了执行命令的接口,这里有一个execute()方法,所有的命令都必须实现这个接口。
  • ConcreteCommand类(PlaceOrderCommand)是实现了OrderCommand接口的具体命令类。它包含一个接收者(Kitchen)和与请求相关的信息(如订单细节)。execute()方法调用接收者的一个或多个动作来完成其请求。
  • Receiver类(Kitchen)是接收者,它负责具体执行命令的逻辑。会打印出接收到的订单信息。
  • Invoker类(Waiter)调用者或发起者,负责接收命令并在适当的时候执行它。这里的Waiter可以接受命令(通过takeOrder方法)并执行它(通过placeOrder方法)。
  • Client类(Restaurant)是客户端它负责创建具体命令对象,并设置其接收者。这里客户端创建了一个PlaceOrderCommand对象,指定了Kitchen作为接收者和订单细节,然后创建了一个Waiter对象,通过它来接收并执行命令。

适用场景

  1. 操作的抽象和实现分离:当需要将发起操作的责任和执行操作的责任分开时,命令模式提供了一种解耦的方式。这样,发起操作的一方不需要知道接收者是谁,以及它是如何处理请求的。
  2. 支持撤销(Undo)操作:命令模式通过记录历史命令来实现撤销功能。每当用户执行一个操作时,系统都会将该操作封装成一个命令对象并保存在历史记录中。如果需要撤销,系统可以从历史记录中取出命令并执行其撤销方法。
  3. 支持日志记录操作:在需要记录和存储操作记录以便于后续的恢复或重放操作时,命令模式可以将所有的操作以命令的形式保存在日志中。在系统恢复或重启后,可以通过执行这些命令来恢复之前的状态。
  4. 队列请求:命令模式可以用来对请求进行排队处理,当请求发送者不需要立即得到结果,或者发送者和执行者有不同的生命周期时,可以通过命令模式来进行解耦。例如,在后台操作或网络请求中,请求可以被封装成命令对象,放入队列中,然后按顺序执行。
  5. 参数化对象:当需要将执行操作的动作作为对象存储、传递和调用时,命令模式提供了一种方式。这样可以在运行时更改方法的调用,增加新的命令,而不需要改变原有代码。
  6. 复合命令:命令模式可以用来组合多个命令,形成复合命令。这对于实现宏命令(Macro Command)非常有用,即一个命令包含了多个子命令的集合,当执行这个宏命令时,可以依次执行所有子命令。
  7. GUI按钮和菜单操作:在图形用户界面中,按钮点击或菜单选择的操作往往与特定的命令对象关联。这样,当用户交互时,相应的命令对象被执行,从而实现了操作的请求。

命令模式的灵活性和解耦能力使其成为软件设计中广泛使用的模式之一,特别是在需要将请求的发送者和接收者解耦,以及需要支持撤销、日志记录和事务功能的场景中。