一文理解设计模式–命令模式(Command)

导读:本篇文章讲解 一文理解设计模式–命令模式(Command),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

命令模式(Command)

命令模式就是将命令包装成java对象,来保存命令历史记录和恢复上一个状态、重复执行命令,比如我们写代码的撤销(undo)、取消撤销(redo)

适用场景

  • 需要undoredo功能,每个命令具有记录状态功能,比如代码写错了撤销、取消撤销
  • 多个命令依据不同情况排序,根据排序后的命令组执行
  • 在系统崩溃时可以重新恢复原来的状态。将命令持久化到日志,在崩溃后恢复需要从磁盘重新加载已记录的命令,并使用execute操作重新执行它们

角色说明

在这里插入图片描述

  • Command:抽象命令类,所有命令需要实现或继承
  • ConcreteCommand:具体命令实现类
  • Invoke:调用者,命令操控者
  • Receiver:命令接收者(命令就是在这个对象生效)

代码示例

通过演示一个妖怪隐身、缩小、恢复原样的过程,来熟悉命令模式

  • 命令抽象类
/**
 * 命令抽象类,像编写命令一样操作
 * 每个命令都可以恢复执行命令之前的状态
 *
 * @author July
 * @date 2020/10/22
 */
public abstract class Command {

    public abstract void execute(Receiver receiver);

    public abstract void undo();

    public abstract void redo();

    @Override
    public abstract String toString();

}
  • 让妖怪隐身的命令

/**
 * 隐形咒是一种具体的命令
 */
public class InvisibilityCommand extends Command {

    private Receiver receiver;

    @Override
    public void execute(Receiver receiver) {
        receiver.setVisibility(Visibility.INVISIBLE);
        this.receiver = receiver;
    }

    @Override
    public void undo() {
        if (receiver != null) {
            receiver.setVisibility(Visibility.VISIBLE);
        }
    }

    @Override
    public void redo() {
        if (receiver != null) {
            receiver.setVisibility(Visibility.INVISIBLE);
        }
    }

    @Override
    public String toString() {
        return "invisible";
    }
}
  • 让妖怪变小的命令
public class ShrinkCommand extends Command {

    private Size oldSize;
    private Receiver receiver;

    @Override
    public void execute(Receiver receiver) {
        oldSize = receiver.getSize();
        receiver.setSize(Size.SMALL);
        this.receiver = receiver;
    }

    @Override
    public void undo() {
        if (oldSize != null && receiver != null) {
            Size temp = receiver.getSize();
            receiver.setSize(oldSize);
            oldSize = temp;
        }
    }

    @Override
    public void redo() {
        undo();
    }

    @Override
    public String toString() {
        return "small";
    }
}
  • 命令接收者的抽象类
public abstract class Receiver {

    private Size size;

    private Visibility visibility;

    public Size getSize() {
        return size;
    }

    public void setSize(Size size) {
        this.size = size;
    }

    public Visibility getVisibility() {
        return visibility;
    }

    public void setVisibility(Visibility visibility) {
        this.visibility = visibility;
    }

    @Override
    public abstract String toString();

    /**
     * Print status
     */
    public void printStatus() {
        System.out.println("接收者状态:" + String.format("[size=%s] [visibility=%s]", getSize(),
                getVisibility()));
        System.out.println();
    }
}
  • 妖怪
/**
 * 妖怪,接收变小和隐身的命令
 */
public class GoblinReceiver extends Receiver {

    public GoblinReceiver() {
        setSize(Size.NORMAL);
        setVisibility(Visibility.VISIBLE);
    }

    @Override
    public String toString() {
        return "GoblinReceiver";
    }

}
  • 控盘者(调用者)
/**
 * 【关键的角色】命令的调用程序
 *
 * @author July
 * @date 2020/10/22
 */
public class Invoker {

    /**
     * 用stack记录
     */
    private Deque<Command> undoStack = new LinkedList<>();
    private Deque<Command> redoStack = new LinkedList<>();

    public Invoker() {
    }

    /**
     * Cast spell
     */
    public void call(Command command, Receiver receiver) {
        System.out.println(this + "在" + receiver + " 上执行命令 " + command);
        command.execute(receiver);
        undoStack.offerLast(command);
    }

    /**
     * 撤销最后的操作
     */
    public void undo() {
        if (!undoStack.isEmpty()) {
            Command previousSpell = undoStack.pollLast();
            redoStack.offerLast(previousSpell);
            System.out.println(this + " undoes " + previousSpell + " command");
            previousSpell.undo();
        }
    }

    /**
     * 重做最后的操作(重新执行命令)
     */
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command previousSpell = redoStack.pollLast();
            undoStack.offerLast(previousSpell);
            System.out.println(this + " redoes " + previousSpell + " command");
            previousSpell.redo();
        }
    }

    @Override
    public String toString() {
        return "Invoker";
    }
}
  • 测试类
public class App {
    
    public static void main(String[] args) {
        // 实例化一个调用角色
        Invoker invoker = new Invoker();
        // 命令执行的接收者(目标)
        GoblinReceiver goblinReceiver = new GoblinReceiver();

        // 打印接收者初始状态
        goblinReceiver.printStatus();

        // 对妖怪执行缩小命令
        invoker.call(new ShrinkCommand(), goblinReceiver);
        goblinReceiver.printStatus();

        // 对妖怪执行隐身命令
        invoker.call(new InvisibilityCommand(), goblinReceiver);
        goblinReceiver.printStatus();

        // 回退到上一步操作
        invoker.undo();
        goblinReceiver.printStatus();

        // 回退到上一步操作
        invoker.undo();
        goblinReceiver.printStatus();

        // 前进一步
        invoker.redo();
        goblinReceiver.printStatus();

        // 前进一步
        invoker.redo();
        goblinReceiver.printStatus();
    }
}

最终的执行结果:

接收者状态:[size=normal] [visibility=visible]

Invoker在GoblinReceiver 上执行命令 small
接收者状态:[size=small] [visibility=visible]

Invoker在GoblinReceiver 上执行命令 invisible
接收者状态:[size=small] [visibility=invisible]

Invoker undoes invisible command
接收者状态:[size=small] [visibility=visible]

Invoker undoes small command
接收者状态:[size=normal] [visibility=visible]

Invoker redoes small command
接收者状态:[size=small] [visibility=visible]

Invoker redoes invisible command
接收者状态:[size=small] [visibility=invisible]

优缺点

优点

  • 降低系统耦合度,通过Invoker解耦Command和Receiver
  • 扩展性高,新命令可以很容易加入到系统中
  • 在不同时间指定、排队和执行请求,可以实现命令队列或宏命令(多个命令组合连续执行)
  • 很容易实现对命令的undoredo

缺点

  • 和设计者模式一样,可能需要大量具体命令类,因为每一个命令都是一个类

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/17886.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!