行为型模式:命令模式

LieBrother原文:
行为型模式:命令模式

景

十一大行为型模式之三:命令模式。

简介

姓名 :命令模式

英文名 :Command Pattern

价值观 :军令如山

个人介绍

Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
(来自《设计模式之禅》)

你要的故事

作为一个程序猿,我们每天都在经历着命令模式,技术经理把需求任务分配给工程师开发,有时因为第三方或者其他不可抗拒的因素导致需求停止开发。这种工作模式就是命令模式。好了,开始故事了。小明在 XX 科技公司做一个安静的程序猿,有一天技术经理给他分配了一个任务:新增黑名单,也就是在他们系统的某个模块里面可以手工对电话打黑名单标签的功能。小明接到任务后就立马开发,在开发了 2 天之后,因为战略原因,技术经理大明暂停了这个开发任务,接下来我们通过非命令模式和命令模式 2 种代码实现来体现这个过程。在这个场景中,为了简单,我们假定技术经理大明手下只有小明一个开发人员。

非命令模式

非命令模式也就是不使用命令模式的代码实现。代码中,我们出现了 Developer 开发人,开发同学是接受技术经理传达的任务,技术经理让他开发哪个需求就开发哪个需求,如果项目有问题需要中断,也需要技术经理评估后传达给开发同学,所以 Developer 有 2 个方法,分别是 develop() 开发需求和 suspend() 暂停需求。 Requirement 则为需求类,TechnicalManager1 则为技术经理类,他有一个方法 action(),通过这个方法来指定开发同学开发任务或者暂停任务。

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
public class NoCommandTest {

public static void main(String[] args) {
Developer xiaoMing = new Developer("小明");
Requirement requirement = new Requirement("新增黑名单");
TechnicalManager1 technicalManager2 = new TechnicalManager1("大明");
technicalManager2.setDeveloper(xiaoMing);
technicalManager2.action(requirement, "develop");
System.out.println("开发了 2 天,需求变故,需要暂停。。。");
technicalManager2.action(requirement, "suspend");
}

}

/**
* 开发人员
*/
class Developer {

private String name;

public Developer(String name) {
this.name = name;
}

public void develop(Requirement requirement) {
System.out.println(this.name + " 开始开发需求:" + requirement.getName());
}

public void suspend(Requirement requirement) {
System.out.println(this.name + " 停止开发需求:" + requirement.getName());
}

public String getName() {
return name;
}

}

/**
* 需求
*/
class Requirement {
private String name;

public Requirement(String name) {
this.name = name;
}

public String getName() {
return name;
}

}

/**
* 技术经理
*/
class TechnicalManager1 {

private String name;

private Developer developer;

public TechnicalManager1(String name) {
this.name = name;
}

public void setDeveloper(Developer developer) {
this.developer = developer;
}

public void action(Requirement requirement, String type) {
if ("develop".equals(type)) {
this.developer.develop(requirement);
} else if ("suspend".equals(type)) {
this.developer.suspend(requirement);
}
}

}

打印结果:
小明 开始开发需求:新增黑名单
开发了 2 天,需求变故,需要暂停。。。
小明 停止开发需求:新增黑名单

通过代码,我们可以发现技术经理和开发同学是强依赖关系。如果技术经理下达了一个任务,要求小明写一下周报,这时候得怎么写?是不是小明需要一个写周报的方法,大明也需要新增一个处理事务类型?有没有更好的方法让技术经理不需要做任何改变?命令模式就来解决这个问题。

命令模式

在这个例子中,不管大明叫小明做什么事情,其实都是一样的,就是下达任务命令,让小明去执行命令。我们可以利用命令模式把下达任务这个抽象起来,当做父类,下达开发命令、下达暂停命令、下达写周报等等都是不同的子命令。代码如下。

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
public class CommandTest {

public static void main(String[] args) {
Developer xiaoMing = new Developer("小明");
Command developCommand = new DevelopCommand(xiaoMing);
Command suspendCommand = new SuspendCommand(xiaoMing);
Requirement requirement = new Requirement("新增黑名单");
TechnicalManager2 technicalManager = new TechnicalManager2("大明");
technicalManager.setCommand(developCommand);
technicalManager.action(requirement);
System.out.println("开发了 2 天,需求变故,需要暂停。。。");
technicalManager.setCommand(suspendCommand);
technicalManager.action(requirement);

}

}

/**
* 命令
*/
abstract class Command {

protected Developer developer;

public Command(Developer developer) {
this.developer = developer;
}

public abstract void execute(Requirement requirement);
}

/**
* 开始开发
*/
class DevelopCommand extends Command {

public DevelopCommand(Developer developer) {
super(developer);
}

@Override
public void execute(Requirement requirement) {
this.developer.develop(requirement);
}
}

/**
* 开发中断
*/
class SuspendCommand extends Command {

public SuspendCommand(Developer developer) {
super(developer);
}

@Override
public void execute(Requirement requirement) {
this.developer.suspend(requirement);
}
}

/**
* 技术经理
*/
class TechnicalManager2 {

private String name;
private Command command;

public TechnicalManager2(String name) {
this.name = name;
}

public void action(Requirement requirement) {
this.command.execute(requirement);
}

public void setCommand(Command command) {
this.command = command;
}
}

打印结果:
小明 开始开发需求:新增黑名单
开发了 2 天,需求变故,需要暂停。。。
小明 停止开发需求:新增黑名单

代码中用 Command 来抽象下达任务,而技术经理 TechnicalManager2 并没有和 Developer 有直接的关系,而是 TechnicalManager2 和 Command 建立的联系,Command 和 Developer 建立了联系。这样子把大明和小明的强依赖关系给剥离开,而新增一个下达写周报的任务也很简单,在 Developer 中新增一个处理写周报的方法,新增一个写周报的 Command 子类,就可以了,TechnicalManager2 如上面所愿不用修改。这就是完整的一个命令模式代码。

代码:
Command Pattern

总结

从文章中我们就可以看到,利用命令模式能够进行类的解耦,让调用者和接受者没有任何关系,也通过对行为的抽象,让新增其他行为变得清晰容易,也就是可扩展性大大增加

参考资料:《大话设计模式》、《设计模式之禅》

推荐阅读:

公众号之设计模式系列文章

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。