抽丝剥茧——命令设计模式

136 阅读5分钟

命令设计模式

兄弟们好,今天和大家聊聊命令设计模式。

我们先来聊聊这个设计模式在哪里使用到了。

  • doundo。我们都知道Mysql数据库的数据恢复依赖于undo日志文件,通过记录用户的操作来实现对数据的回滚操作
  • 我们常常用到的CTRL + Z操作,也是一个撤销的操作,用它进行撤销我们上一步的操作

它强调的是把用户的每一次操作参数化,并且提供了撤销和恢复的功能。就如Mysql而言,它将用户的每一步操作记录相当于一个参数,提供回滚功能。

我们可以思考一个场景,就拿我们常见的word为例,我们可以通过菜单上面的按钮去修改我们正在编辑文档的格式内容等等。它是怎么实现的?我们通过菜单进行了一系列的命令以后,突然我发现有一处我修改错了,我需要撤销,它又是怎么实现的?

我们来简单的思考一下它的实现方式。首先我需要一个按钮,然后我需要这个按钮绑定一个对应的操作文档的功能,代码表现形式就是通过组合的方式将文档集成到按钮中,这个时候属于直接通过按钮操作了文档。这解决了我们通过按钮实现修改文档的需求,但如何撤回呢?这样做似乎无法实现我们撤回的需求

想要撤回,路只有一条,就是记录我们的操作,所以我们可以创建一个执行器,帮助我们记录我们所有的文档操作,然后执行,当我们想要撤销的时候,删除最后一个命令即可。

思想很简单,但是我们要将这个执行其和我们的按钮,文档整合到一起。而在我们上面的思想中,每一个按钮对应一个文档操作,所以我们需要对按钮的操作进行一个抽象,我们需要一个命令接口,代表一个执行的功能,所有的操作按钮功能都实现这个接口,进行具体的方法执行。同时在我们的执行器中,我们聚合一个按钮的抽象功能接口的集合就可以了,当我们想要撤销的时候直接移除最后一个,然后重新执行一次集合中的命令即可

这个就是一个完整的命令模式的实现思路。下面我们集合上面的案例,来书写代码和画具体的类图实现。

不过在这之前,我们得从上面得案例中将命令模式的角色构成总结出来:

  • Invoke。执行者,对外暴露的接口,用户操作他实现业务调用
  • Command。抽象类或者接口,提供需要被调用的方法
  • ConcreteCommand。抽象类或者接口的实现类,实现具体的调用逻辑
  • Receiver。接收者,接收具体的命令并且执行命令

我们来看一下它的类图实现

我们再来看它的代码实现:

接收者,文档对象

//接收命令
class Document{
    public void create(){
        System.out.println("Create");
    }

    public void open(){
        System.out.println("open");
    }

    public void close(){
        System.out.println("Close");
    }

}

按钮,命令实现类

interface DocumentCommand{
    void execute();
}


class CreateCommand implements DocumentCommand{

    private Document document ;

    public CreateCommand(Document document){
        this.document = document ;
    }


    @Override
    public void execute() {
        document.close();
    }
}

class CloseCommand implements DocumentCommand{

    private Document document ;

    public CloseCommand(Document document){
        this.document = document ;
    }


    @Override
    public void execute() {
        document.close();
    }
}

class OpenCommand implements DocumentCommand{

    private Document document ;

    public OpenCommand(Document document){
        this.document = document ;
    }


    @Override
    public void execute() {
        document.open();
    }
}

调用者

class UserInvoke{
    private List<DocumentCommand> lists ;

    public UserInvoke(){
        lists = new ArrayList<>();
    }

    public void addCommand(DocumentCommand documentCommand){
        lists.add(documentCommand);
    }

    public void removeCommand(DocumentCommand documentCommand){
        lists.remove(documentCommand);
    }

    public void execute(){
        for (DocumentCommand list : lists) {
            list.execute();
        }
    }

}

具体的调用

public static void main(String[] args) {
        Document document = new Document();
        UserInvoke userInvoke = new UserInvoke();
        userInvoke.addCommand(new CloseCommand(document));
        userInvoke.addCommand(new OpenCommand(document));
    }

好啦,今天的设计模式,就到这里了。兄弟们,晚安啊。现在是23点57分,发完文章估计就12点了。第二天,热情依旧,冲。