Luckylau's Blog

设计模式之命令模式

​ 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的处理对象,并把该命令传给这个处理对象,该处理对象执行命令。命令模式把发出命令的责任和执行命令的责任分开,委派个不同的对象。

我们先考虑发出命令和执行命令耦合起来的情况,看有啥不好

1
2
3
4
5
6
7
8
9
10
// 以下逻辑位于“命令发出者”的方法内:
if(要画形状){
形状处理对象.画形状("圆形");
}
if(要填充颜色){
颜色处理对象.填充颜色("红色");
}
if(要组合形状){
组合处理对象.组合形状([]{"红色圆形","蓝色矩形","黄色三角形");
}

“命令发出者”要同所有的“命令执行者”维持关系(前者要有后者的所有引用),只要增加一种命令执行者,就需要修改命令发出者的代码逻辑,显然不符合“开闭”原则的吧;尤其是命令发出者如果是不开放修改的(比如位于框架中),就很难保证扩展性了;命令执行有可能失败,或者Ctrl+Z回退,这种操作显然是经常遇到的,对于”命令发出者“的逻辑复杂度来说,显然又是雪上加霜。

我们再来看一下,应用命令模式如何解耦以及解耦后有什么好处。

将发出命令和执行命令解耦,即“发出命令者”不直接调用“执行命令者”的方法,而是通过一个“中间媒介”,这个“中间媒介”就是“命令”。从而由原来的发出命令者 - 执行命令者的关系变成发出命令者 - 命令 - 执行命令者

发出命令者 DrawingApp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DrawingApp {
private List<Command> commands = new ArrayList<Command>();
public void takeCommand(Command command) {
commands.add(command);
}
public void CommandsDone() {
for (Command command:commands) {
command.doCmd();
}
}
public void undoLastCommand() {
commands.get(commands.size() - 1).undoCmd();
}
}

命令抽象 Command.java

1
2
3
4
public interface Command {
void doCmd();
void undoCmd();
}

具体命令 ShapeDrawing.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ShapeDrawing implements Command {
private ShapeDrawer drawer;
private String arg;
public ShapeDrawing(ShapeDrawer drawer, String arg) {
this.drawer = drawer;
this.arg = arg;
}
public void doCmd() {
drawer.drawShape(arg);
}
public void undoCmd() {
drawer.undrawShape();
}
}

具体命令 ColorFilling.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ColorFilling implements Command {
private ColorFiller filler;
private String arg;
public ColorFilling(ColorFiller filler, String arg) {
this.filler = filler;
this.arg = arg;
}
public void doCmd() {
filler.fillColor(arg);
}
public void undoCmd() {
filler.unfillColor();
}
}

执行命令者 ShapeDrawer.java

1
2
3
4
5
6
7
8
public class ShapeDrawer {
public void drawShape(String shape) {
System.out.println("画了一个" + shape);
}
public void undrawShape() {
System.out.println("撤销刚才画的形状");
}
}

执行命令者 ColorFiller.java

1
2
3
4
5
6
7
8
public class ColorFiller {
public void fillColor(String color) {
System.out.println("填充" + color);
}
public void unfillColor() {
System.out.println("撤销填充的颜色");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
DrawingApp app = new DrawingApp();
ShapeDrawer shapeDrawer = new ShapeDrawer();
ColorFiller colorFiller = new ColorFiller();
Command drawCircle = new ShapeDrawing(shapeDrawer, "圆形");
Command fillRed = new ColorFilling(colorFiller, "红色");
Command drawRectancle = new ShapeDrawing(shapeDrawer, "矩形");
app.takeCommand(drawCircle);
app.takeCommand(fillRed);
app.takeCommand(drawRectancle);
app.CommandsDone();
app.undoLastCommand();
}
}
Luckylau wechat
如果对您有价值,看官可以打赏的!