设计模式14-Command

本文最后更新于:3 years ago

命令模式

  命令模式(Command Pattern)是一种数据驱动的设计模式,它属于 行为型模式 。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

如果想了解命令模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-命令模式

结构图


优缺点

优点:

1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。

缺点:

使用命令模式可能会导致某些系统有过多的具体命令类。

使用场景

认为是命令的地方都可以使用命令模式,比如:
1、GUI 中每一个按钮都是一条命令。
2、模拟 CMD。

实现代码

Command模式可以说在我们日常生活中用得比较多的了,命令模式主要解决我们在开发过程中,请求者与实现者的解耦

举个栗子,比如空调遥控器,我们在使用空调遥控器的时候,只需要按下相应的键就可以完成温度的调控,这就是命令模式
在这里温度调控请求和温度调控处理完全解耦了,空调遥控器(命令发送者)通过按钮(具体命令)来遥控空调(命令接收者)

其实写到现在会发现很多模式其原理都是很类似的,只是每个模式的作用不同罢了

既然提到遥控器这个栗子,那就用这个例子来介绍一下吧

首先定义一个Command的接口,接口中有一个execute的抽象类方法
再创建一个RemoteControl的遥控器类,来实现Command接口,并且在RemoteControl中定义一个AirConditioner类型的变量,用于实现温度调控
接着我们再创建一个Invoker类来模拟用户,其中定义一个Command,无参构造用于接收传入的Command,并且定义call方法用于调用传入Command中的execute方法(这里就实现了请求者(Invoker)与实现者(AirConditioner)的解耦)

Code:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Main {
public static void main(String[] args) {
Command cmd = new RemoteControl();
Invoker iv = new Invoker(cmd);
System.out.println("已按下按键...");
iv.call();
}
}

public interface Command {
public abstract void execute();
}

public class RemoteControl implements Command{
private AirConditioner airCon;

public RemoteControl (){
airCon = new AirConditioner();
}

@Override
public void execute() {
airCon.action();
}
}

//实现者
public class AirConditioner {
public void action(){
System.out.println("温度已改变...");
}
}

//调用者
public class Invoker {
private Command command;

public Invoker (Command command){
this.command = command;
}

public void call(){
System.out.println("温度调控命令已执行...");
command.execute();
}
}

控制台输出如下:

1
2
3
已按下按键...
温度调控命令已执行...
温度已改变...

以上就是命令模式的代码,很简单
接着我在补充一个常用到的一个例子,Undo的实现

拿记事本来讲吧,我们在往记事本中编写东西的时候,有时候会撤销一些之前我们做过的操作(这里可以理解为Ctrl+z,但是实际上Ctrl+z是备忘录模式的实现),这就是我们要实现的Undo操作

首先我们创建一个Command接口,含有操作(doIt)和撤销(undo)两个方法
接着继续创建三个类:CopyCommand、DeleteCommand和InsertCommand三个方法分别实现Command接口,
最后创建一个我们需要处理的类Content

Code:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class Main {
public static void main(String[] args) {
Content content = new Content();

Command command = new InsertCommand(content);
command.doIt("Hello");
command.undo();

Command command1 = new DeleteCommand(content);
command1.doIt();
command1.undo();

Command command2 = new CopyCommand(content);
command2.doIt();
command2.undo();
}
}

public interface Command {
public abstract void doIt();
public abstract void undo();
}

public class CopyCommand implements Command{
Content c;
String strToCopy;
public CopyCommand(Content c){
this.c = c;
}

@Override
public void doIt() {
c.msg = c.msg + c.msg;
System.out.println(c.msg);
}

@Override
public void undo() {
c.msg = c.msg.substring(0, c.msg.length()/2);
System.out.println(c.msg);
}
}

public class DeleteCommand implements Command{
Content c;
String strToDelete;
public DeleteCommand(Content c){
this.c = c;
}

@Override
public void doIt() {
strToDelete = c.msg.substring(0,5);
c.msg = c.msg.substring(5, c.msg.length());
System.out.println(c.msg);
}

@Override
public void undo() {
c.msg = strToDelete + c.msg;
System.out.println(c.msg);
}
}

public class InsertCommand implements Command{
Content c;
String strToInsert;
public InsertCommand(Content c){
this.c = c;
}

@Override
public void doIt(String str) {
strToInsert = str;
c.msg = c.msg + str;
System.out.println(c.msg);
}

@Override
public void undo() {
c.msg = c.msg.substring(0,c.msg.length()-strToInsert.length());
System.out.println(c.msg);
}
}

public class Content {
String msg = "Welcome ";
}

CopyCommand中,我们实现复制的命令,其中doIt方法用于复制传入的内容,而undo则实现撤销复制的操作;
DeleteCommand中,我们实现删除的命令,其中doIt方法用于删除前五个字符,并且保留前五个字符用于undo的撤销操作;
InsertCommand中,我们实现插入的命令,其中doIt方法用于插入传入的字符串,undo用于撤销刚刚的插入

输出结果:

1
2
3
4
5
6
Welcome Hello
Welcome
me
Welcome
Welcome Welcome
Welcome

这里就简单实现了一下一些操作的撤销功能,而真实的情况是,我们需要记录多条undo,因为我们可能需要多次撤销,此时我们这里的操作就不满足需求了
解决方式就需要将命令模式和责任链模式结合使用,将用过的Command都放到一个容器中,接着就可以实现多个undo了(代码就不写了,提供思路)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!