命令模式实现undo和redo

命令模式

定义与类型

  • 定义: 命令模式将一个请求封装成对象,让用户使用不同的请求将客户端参数化。
  • 类型: 行为型

适用场景

  • 想用对象参数化一个动作以执行操作,并且用不同命令对象来替换回调函数
  • 应用程序支持撤销和恢复
  • 记录请求日志,当系统故障这些命令可以重新被执行

优点

  • 能较容易的设计一个命令队列
  • 在需要的情况下,可以容易的将命令计入日志
  • 允许接收请求的一方决定是否否决请求
  • 可以容易的实现对请求的撤销和重做
  • 由于加进新的具体命令类不影响其他的类,因此便于扩展
  • 命令模式将请求一个操作的对象与具体执行一个操作的对象分割开,符合开闭原则和迪米特法则

缺点

角色划分

  • 角色一:接收者(Receiver作用:负责具体的功能实现->具体实现细节)(非必须,也可以是个方法)
  • 角色二:命令接口(协议)(Command:命令抽象)
  • 角色三:具体的命令(ConcreteCmmand:具体的命令,作用:负责调用接收者逻辑方法,行为方法)
  • 角色四:请求者角色(Invoker:调用执行命令)
  • 角色五:客户端

结构图

引用一下大话设计模式中的结构图

提出需求

实现undo和redo的功能

分析需求

undo好redo的功能的本质是命令队列中的命令的规律执行。闲言碎语不要讲,咱们直接开撸。

代码结构

SQLExcute扮演的是Receiver,CommandManager扮演Invoke,其他的两个角色就一目了然了。我们看下每个类具体的代码。

  • ICommand
/**
 * @Author: ming.wang
 * @Date: 2019/3/7 16:42
 * @Description:
 */
public interface ICommand {

    void uodo();

    void excute();

}
  • InsertIntoCommand
/**
 * @Author: ming.wang
 * @Date: 2019/3/11 17:23
 * @Description:
 */
@Getter
@Setter
@ToString
public class InsertIntoCommand implements ICommand {

    private SQLExcute sqlExcute;

    private String id;

    public InsertIntoCommand(SQLExcute sqlExcute, String id) {
        this.sqlExcute = sqlExcute;
        this.id = id;
    }

    @Override
    public void uodo() {
        sqlExcute.delete(id);
    }

    @Override
    public void excute() {
        sqlExcute.insertInto(id);
    }
}
  • SQLExcute
/**
 * @Author: ming.wang
 * @Date: 2019/3/12 9:52
 * @Description:
 */
@Getter
@Setter
@ToString
public class SQLExcute {

    public void insertInto(String id) {
        System.out.println("插入一条数据,id:" + id);
    }

    public void delete(String id) {
        System.out.println("删除一条数据,id:" + id);
    }
}
  • CommandManager
/**
 * @Author: ming.wang
 * @Date: 2019/3/11 17:26
 * @Description:
 */
public class CommandManager {

    private Stack<ICommand> redoStacks = new Stack<>();
    private Stack<ICommand> undoStacks = new Stack<>();

    public void excute(ICommand iCommand) {
        iCommand.excute();
        undoStacks.push(iCommand);
        if (!redoStacks.isEmpty()) {
            redoStacks.clear();
        }
    }

    public void uodo() {
        if (!undoStacks.isEmpty()) {
            ICommand pop = undoStacks.pop();
            pop.uodo();
            redoStacks.push(pop);
        }
    }

    public void redo() {
        if (!redoStacks.isEmpty()) {
            ICommand pop = redoStacks.pop();
            pop.excute();
        }
    }

}
  • Test
/**
 * @Author: ming.wang
 * @Date: 2019/3/11 16:44
 * @Description:
 */
public class Test {
    public static void main(String[] args) throws InterruptedException {
        CommandManager commandManager = new CommandManager();
        SQLExcute sqlExcute = new SQLExcute();
        InsertIntoCommand intoCommand1 = new InsertIntoCommand(sqlExcute, "1");
        InsertIntoCommand intoCommand2 = new InsertIntoCommand(sqlExcute, "2");
        System.out.println("执行命令");
        commandManager.excute(intoCommand1);
        commandManager.excute(intoCommand2);

        System.out.println("执行undo");
        commandManager.uodo();
        commandManager.uodo();

        System.out.println("执行redo");
        commandManager.redo();
        commandManager.redo();

    }
}

运行结果

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容