设计模式 - 命令模式

95 阅读4分钟

设计模式 - 命令模式

一、引入

当你在玩电视遥控器时,你按下一个按钮,电视就会打开。你不需要了解电视是如何工作的,只需要知道按下按钮就能实现你想要的功能。命令模式就好像是一个遥控器,它帮助你将一个请求(比如打开电视)封装成一个对象,然后通过这个对象来执行请求,而无需了解具体的实现细节。

二、概念

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使得可以参数化、队列化、记录日志或者撤销请求。

在命令模式中,命令对象包含了一个接收者和一组操作,它可以调用接收者的方法来完成特定的任务。

三、基本结构

  1. 命令接口(Command):定义了执行命令的接口。
  2. 具体命令(Concrete Command):实现了命令接口,包含了对接收者的引用和执行操作。
  3. 接收者(Receiver):负责执行命令的具体操作。
  4. 调用者(Invoker):负责调用命令并执行它。
  5. 客户端(Client):创建具体命令对象并配置它的接收者。

四、示例代码

假设我们有一个简单的遥控器,可以控制电视机的开关、音量调节和频道切换。我们可以使用命令模式来实现这个功能。

命令接口

/**
 * 命令接口
 */
public interface Command {
    void execute();
}

具体的命令:关闭电视、打开电视、调节音量

/**
 * 关闭电视命令
 */
public class TurnOffCommand implements Command {
    private Television television;

    public TurnOffCommand(Television television) {
        this.television = television;
    }

    @Override
    public void execute() {
        television.turnOff();
    }
}

/**
 * 打开电视命令
 */
public class TurnOnCommand implements Command {
    private Television television;

    public TurnOnCommand(Television television) {
        this.television = television;
    }

    @Override
    public void execute() {
        television.turnOn();
    }
}

/**
 * 调节音量命令
 */
public class AdjustVolumeCommand implements Command {
    private Television television;
    private Integer volume;

    public AdjustVolumeCommand(Television television, Integer volume) {
        this.television = television;
        this.volume = volume;
    }

    @Override
    public void execute() {
        television.adjustVolume(volume);
    }
}

接受者

/**
 * 接受者
 */
public class Television {

    public void turnOn() {
        System.out.println("打开电视机");
    }

    public void turnOff() {
        System.out.println("关闭电视机");
    }

    public void adjustVolume(int volume) {
        System.out.println("调节音量 " + volume);
    }
}

调用者

/**
 * 调用者
 */
public class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        Television television = new Television();
        TurnOnCommand turnOnCommand = new TurnOnCommand(television);
        TurnOffCommand turnOffCommand = new TurnOffCommand(television);
        AdjustVolumeCommand adjustVolumeCommand = new AdjustVolumeCommand(television, 20);

        RemoteControl remoteControl = new RemoteControl();

        //调用打开电视命令
        remoteControl.setCommand(turnOnCommand);
        remoteControl.pressButton();

        //调用关闭电视命令
        remoteControl.setCommand(turnOffCommand);
        remoteControl.pressButton();

        //调用调节音量命令
        remoteControl.setCommand(adjustVolumeCommand);
        remoteControl.pressButton();
    }
}

五、能干什么

  1. 封装操作:将一个请求封装成一个对象,从而使得可以参数化和传递请求,以及支持撤销和重做操作。
  2. 解耦调用者和接收者:调用者无需知道接收者的具体信息,只需要通过命令对象来执行请求。
  3. 支持撤销和重做:由于命令对象可以记录操作历史,可以实现撤销和重做功能。
  4. 支持事务:可以将多个操作封装成一个命令对象,从而实现事务。
  5. 支持队列请求:可以将命令对象放入队列中,实现异步处理请求。
  6. 支持日志和记录:可以记录所有执行的命令,用于日志记录或者回放。

六、总结

优点:

  1. 降低系统耦合度:命令模式将请求发送者和接收者解耦,使得系统更加灵活,可以方便地扩展和维护。
  2. 支持撤销和重做:由于命令对象可以记录操作历史,可以实现撤销和重做功能。
  3. 支持事务处理:可以将多个操作封装成一个命令对象,从而实现事务处理。
  4. 支持队列请求:可以将命令对象放入队列中,实现异步处理请求。
  5. 支持日志和记录:可以记录所有执行的命令,用于日志记录或者回放。
  6. 容易扩展新的命令:可以通过添加新的具体命令类来扩展系统的功能。

缺点:

  1. 会增加类的数量:每个具体命令类都需要一个单独的类来实现,可能会增加系统的复杂性。
  2. 可能会导致过多的具体命令类:如果系统中有大量的具体命令类,可能会导致类的数量过多,不利于维护。
  3. 可能会增加系统开销:每个具体命令类都需要一个单独的对象来维护,可能会增加系统的内存开销。