命令模式(Command Pattern)

命令模式(Command Pattern)

命令模式是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化。命令模式允许你将请求的发送者和接收者解耦,支持请求的排队、记录日志以及撤销操作。

模式结构

  1. 命令接口(Command)
    定义执行操作的接口,通常包含一个 execute() 方法。
  2. 具体命令类(Concrete Command)
    实现命令接口,封装具体的操作。它通常持有一个接收者对象,并将请求转发给接收者。
  3. 接收者(Receiver)
    负责执行具体的操作。命令对象会将请求转发给接收者。
  4. 调用者(Invoker)
    持有命令对象,并调用命令对象的 execute() 方法来执行请求。
  5. 客户端(Client)
    创建命令对象并设置其接收者,然后将命令对象传递给调用者。

代码示例

1. 命令接口(Command)

// 命令接口:定义执行操作的接口
interface Command {
    void execute();
}

2. 具体命令类(Concrete Command)

// 具体命令类:打开文件命令
class OpenFileCommand implements Command {
    private final FileSystemReceiver receiver;

    public OpenFileCommand(FileSystemReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.openFile();
    }
}

// 具体命令类:写入文件命令
class WriteFileCommand implements Command {
    private final FileSystemReceiver receiver;

    public WriteFileCommand(FileSystemReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.writeFile();
    }
}

// 具体命令类:关闭文件命令
class CloseFileCommand implements Command {
    private final FileSystemReceiver receiver;

    public CloseFileCommand(FileSystemReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.closeFile();
    }
}

3. 接收者(Receiver)

// 接收者接口:定义执行操作的方法
interface FileSystemReceiver {
    void openFile();
    void writeFile();
    void closeFile();
}

// 具体接收者:Unix 文件系统接收者
class UnixFileSystemReceiver implements FileSystemReceiver {
    @Override
    public void openFile() {
        System.out.println("在 Unix 系统中打开文件 ...");
    }

    @Override
    public void writeFile() {
        System.out.println("在 Unix 系统中写入文件 ...");
    }

    @Override
    public void closeFile() {
        System.out.println("在 Unix 系统中关闭文件 ...");
    }
}

// 具体接收者:Windows 文件系统接收者
class WindowsFileSystemReceiver implements FileSystemReceiver {
    @Override
    public void openFile() {
        System.out.println("在 Windows 系统中打开文件 ...");
    }

    @Override
    public void writeFile() {
        System.out.println("在 Windows 系统中写入文件 ...");
    }

    @Override
    public void closeFile() {
        System.out.println("在 Windows 系统中关闭文件 ...");
    }
}

4. 调用者(Invoker)

// 调用者:持有命令对象并调用其 execute() 方法
class FileInvoker {
    private final Command command;

    public FileInvoker(Command command) {
        this.command = command;
    }

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

5.代码示例

public class CommandPattern {
    public static void main(String[] args) {
        // 根据操作系统选择接收者
        String osName = System.getProperty("os.name");
        System.out.println("当前操作系统: " + osName);

        FileSystemReceiver receiver;
        if (osName.contains("Windows")) {
            receiver = new WindowsFileSystemReceiver();
        } else {
            receiver = new UnixFileSystemReceiver();
        }

        // 创建命令对象
        Command open = new OpenFileCommand(receiver);
        Command write = new WriteFileCommand(receiver);
        Command close = new CloseFileCommand(receiver);

        // 创建调用者并执行命令
        FileInvoker invoker = new FileInvoker(open);
        invoker.execute();

        invoker = new FileInvoker(write);
        invoker.execute();

        invoker = new FileInvoker(close);
        invoker.execute();
    }
}

输出结构

当前操作系统: Windows
在 Windows 系统中打开文件 ...
在 Windows 系统中写入文件 ...
在 Windows 系统中关闭文件 ...

应用场景

  1. 参数化对象
    当需要通过操作来参数化对象时,可以使用命令模式。命令模式将特定的方法调用转化为独立对象,使得请求的发送者和接收者解耦。
  2. 请求排队或记录日志
    当需要将操作放入队列中、记录操作日志或远程执行操作时,可以使用命令模式。命令对象可以被序列化并保存,方便后续恢复或传输。
  3. 撤销操作
    当需要实现操作的撤销功能时,可以使用命令模式。命令模式可以通过保存操作的历史记录来实现撤销功能。

在Java中的应用

  • java.lang.Runnable

    Java 中的 Runnable 接口就是一个典型的命令模式应用。Runnable 接口定义了一个 run() 方法,具体的任务可以通过实现 Runnable 接口来封装。

优缺点

优点

  1. 解耦请求的发送者和接收者
    命令模式将请求的发送者和接收者解耦,使得请求的发送者不需要知道具体的接收者。
  2. 支持撤销和恢复操作
    命令模式可以通过保存操作的历史记录来实现撤销和恢复功能。
  3. 支持请求的排队和日志记录
    命令模式可以将请求封装为对象,方便请求的排队和日志记录。

缺点

  1. 增加代码复杂性
    命令模式引入了额外的类和接口,增加了代码的复杂性。
  2. 可能产生大量具体命令类
    如果系统中需要处理大量的命令,可能会导致具体命令类的数量过多。

总结

命令模式通过将请求封装为对象,使得请求的发送者和接收者解耦。它适用于需要通过操作来参数化对象、支持请求的排队和日志记录、以及实现撤销操作的场景。尽管它可能会增加代码的复杂性,但其优点在于提高了代码的灵活性和可扩展性。在Java中,命令模式广泛应用于任务调度和事件处理等场景。