命令模式

470 阅读2分钟

前言

命令模式是一种高内聚的模式,使用频率其实也不算太高,本质是对命令进行封装,将发出命令的责任和执行命令的责任分离开。

命令模式将一个或一组命令封装为一个对象,从而能够将函数方法作为参数进行传输,同时还能够解耦客户端和服务端的直接耦合,适用场景有:做简单的请求排队,记录请求日志,以及支持可撤销的操作。

目录

一、定义

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

二、模式原理分析

//1.抽象命令类
public interface Command {
    void excute();
}
//2.1 具体命令类
public class Command1 implements Command {

    private final Receiver receiver;

    public Command1(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void excute() {
        receiver.operationA();
    }
}
//2.2 具体命令类
public class Command2 implements Command {

    private final Receiver receiver;

    public Command2(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void excute() {
        receiver.operationB();
    }
}
//2.3 具体命令类
public class Command3 implements Command {

    private final Receiver receiver;

    public Command3(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void excute() {
        receiver.operationC();
    }
}

//3 抽象接收者
public interface Receiver {

    void operationA();

    void operationB();

    void operationC();

}
//4 具体接收者 
public class Receiver1 implements Receiver {

    @Override
    public void operationA() {
        System.out.println("操作 A");
    }

    @Override
    public void operationB() {
        System.out.println("操作 B");
    }

    @Override
    public void operationC() {
        System.out.println("操作 C");
    }
}
//5 调用者
public class Invoker {
    private final List<Command> commands;
    public Invoker() {
        commands = new ArrayList<>();
    }
    public void setCommand(Command command) {
        commands.add(command);
    }
    public void run() {
        commands.forEach(Command::execute);
    }
}
//6. 场景类
public class Demo {

    public static void main(String[] args) {

        Receiver receiver1 = new Receiver1();

        Invoker invoker = new Invoker();

        invoker.setCommand(new Command1(receiver1));
        invoker.setCommand(new Command2(receiver1));
        invoker.setCommand(new Command3(receiver1));
        invoker.run();

    }

}

//输出结果

操作 A
操作 B
操作 C

三、使用场景

  • 只要是认为有命令的地方就可以采用命令模式

四、优点

  • 类间解耦,调用者角色和接收者角色没有依赖关系,调用者只能调用Commandexecute

  • 可扩展性,Command的子类可以非常容易地扩展

  • 命令模式可以结合责任链模式,实现命令族解析任务,结合模板方法,还可以减少Command子类膨胀问题

五、缺点

  • 如果有N个命令,Command的子类就有N个,类膨胀问题严重

  • 不同的接收者可能需要实现重复的命令,例如Markdown需要实现打开、关闭、保存的命令,而非Markdown编辑器也需要实现打开、关闭、保存的命令