Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

  • Java核心基础

  • 设计模式

    • 概念
    • 创建型模式

    • 结构型模式

    • 行为型模式

      • 责任链
      • 命令
        • 概念
        • 问题引入
        • 解决方案
        • 思考
        • 小结
      • 解释器
      • 迭代器
      • 中介
      • 备忘录
      • 观察者
      • 状态
      • 策略
      • 模板方法
      • 访问者
  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • 设计模式
  • 行为型模式
SaulJWu
2021-02-10

命令

# 概念


将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

命令模式(Command)是指,把请求封装成一个命令,然后执行该命令。

# 问题引入

在使用命令模式前,我们先以一个编辑器为例子,看看如何实现简单的编辑操作:

public class TextEditor {
    private StringBuilder buffer = new StringBuilder();

    public void copy() {
        ...
    }

    public void paste() {
        String text = getFromClipBoard();
        add(text);
    }

    public void add(String s) {
        buffer.append(s);
    }

    public void delete() {
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length() - 1);
        }
    }

    public String getState() {
        return buffer.toString();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

我们用一个StringBuilder模拟一个文本编辑器,它支持copy()、paste()、add()、delete()等方法。

正常情况,我们像这样调用TextEditor:

TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();
System.out.println(editor.getState());
1
2
3
4
5

这是直接调用方法,调用方需要了解TextEditor的所有接口信息。

如果改用命令模式,我们就要把调用方发送命令和执行方执行命令分开。怎么分?

# 解决方案

解决方案是引入一个Command接口:

public interface Command {
    void execute();	
}
1
2
3

调用方创建一个对应的Command,然后执行,并不关心内部是如何具体执行的。

为了支持CopyCommand和PasteCommand这两个命令,我们从Command接口派生:

public class CopyCommand implements Command {
    // 持有执行者对象:
    private TextEditor receiver;

    public CopyCommand(TextEditor receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.copy();
    }
}

public class PasteCommand implements Command {
    private TextEditor receiver;

    public PasteCommand(TextEditor receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.paste();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

最后我们把Command和TextEditor组装一下,客户端这么写:

TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
// 执行一个CopyCommand:
Command copy = new CopyCommand(editor);
copy.execute();
editor.add("----\n");
// 执行一个PasteCommand:
Command paste = new PasteCommand(editor);
paste.execute();
System.out.println(editor.getState());
1
2
3
4
5
6
7
8
9
10

这就是命令模式的结构:

┌──────┐      ┌───────┐
│Client│─ ─ ─>│Command│
└──────┘      └───────┘
                  │  ┌──────────────┐
                  ├─>│ CopyCommand  │
                  │  ├──────────────┤
                  │  │editor.copy() │─ ┐
                  │  └──────────────┘
                  │                    │  ┌────────────┐
                  │  ┌──────────────┐   ─>│ TextEditor │
                  └─>│ PasteCommand │  │  └────────────┘
                     ├──────────────┤
                     │editor.paste()│─ ┘
                     └──────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 思考

有的童鞋会有疑问:搞了一大堆Command,多了好几个类,还不如直接这么写简单:

TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();
1
2
3
4

实际上,使用命令模式,确实增加了系统的复杂度。如果需求很简单,那么直接调用显然更直观而且更简单。

那么我们还需要命令模式吗?

答案是视需求而定。如果TextEditor复杂到一定程度,并且需要支持Undo、Redo的功能时,就需要使用命令模式,因为我们可以给每个命令增加undo():

public interface Command {
    void execute();
    void undo();
}
1
2
3
4

然后把执行的一系列命令用List保存起来,就既能支持Undo,又能支持Redo。这个时候,我们又需要一个Invoker对象,负责执行命令并保存历史命令:

┌─────────────┐
│   Client    │
└─────────────┘
       │

       │
       ▼
┌─────────────┐
│   Invoker   │
├─────────────┤    ┌───────┐
│List commands│─ ─>│Command│
│invoke(c)    │    └───────┘
│undo()       │        │  ┌──────────────┐
└─────────────┘        ├─>│ CopyCommand  │
                       │  ├──────────────┤
                       │  │editor.copy() │─ ┐
                       │  └──────────────┘
                       │                    │  ┌────────────┐
                       │  ┌──────────────┐   ─>│ TextEditor │
                       └─>│ PasteCommand │  │  └────────────┘
                          ├──────────────┤
                          │editor.paste()│─ ┘
                          └──────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

可见,模式带来的设计复杂度的增加是随着需求而增加的,它减少的是系统各组件的耦合度。

# 小结

命令模式的设计思想是把命令的创建和执行分离,使得调用者无需关心具体的执行过程。

通过封装Command对象,命令模式可以保存已执行的命令,从而支持撤销、重做等操作

帮我改善此页面 (opens new window)
上次更新: 2021/02/16, 02:24:29
责任链
解释器

← 责任链 解释器→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式