在面向对象程式设计的范畴中,命令模式是一种设计模式,它尝试以物件来代表实际行动。命令物件可以把行动(action) 及其参数封装起来,于是这些行动可以被:
- 重复多次
- 取消(如果该物件有实作的话)
- 取消后又再重做
这些都是现代大型应用程序所必须的功能,即“复原”及“重复”。----WIKIPEDIA
个人理解
命令模式是一个高内聚的模式,它将一个请求封装为一个对象,让你使用不同的请求将客户端参数化,在项目中应用比较广泛,他的封装性比较好,将请求方和接收方分离开来,扩展性较好。命令模式中主要有三种角色,一个是命令的接受者(Receiver),他负责对于收到的命令作出响应。一个是命令(Command)角色,他负责定义接受者需要执行什么样的命令。另一个是调用者(Invoker),接收命令调用并执行命令。但是命令模式也存在不足,如果命令较多的时候那么会存在多个子类,导致臃肿,可以结合其他的模式进行设计,如结合责任链模式实现命令的解析、结合模板方法模式减少子类的膨胀问题。
案例解析
命令模式模板
三个主要角色
1. 接收者(Receiver)
负责具体执行哪些动作,也就是说他只负责提供一些任务的具体实现,不负责其他的部分。
2. 命令(Command)角色
负责定义好具体的命令,包括规定好接收者需要执行的哪些动作。
3. 调用者(Invoker)角色
负责调用命令执行,不需要知道谁是接收者,以及接收者怎么去执行,因为需要知道怎么执行的是接收者,他定义了任务的具体实现,另外不需要知道接收者是谁,这在命令角色中已经定义了哪些接收者执行哪些动作。
命令模式模板类结构图
主要代码
接收者角色:
public interface Receiver {
public void doSomthing();
}
具体实现类
public class Receiver1 implements Receiver {
@Override
public void doSomthing() {
System.out.println("Receiver1");
}
}
命令角色
public abstract class Command {
protected Receiver receiver;
public Command(Receiver receiver) {
this.receiver = receiver;
}
public abstract void execute();
}
具体实现类
public class Command1 extends Command {
public Command1(Receiver receiver) {
super(receiver);
}
@Override
public void execute() {
this.receiver.doSomthing();
}
}
调用者角色
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action() {
this.command.execute();
}
}
测试类
public class Test {
public static void main(String[] args) {
// 调用者
Invoker invoker = new Invoker();
// 接收者
Receiver receiver = new Receiver1();
// 命令角色
Command command = new Command1(receiver);
invoker.setCommand(command);
invoker.action();
}
}
拓展案例解读
案例背景介绍
以家庭雇佣管家和保姆等佣人为例子,主要的人物有,老板、佣人,其中老板在命令模式中相当于调用者,而佣人则相当于接收者,老板对于佣人的指令相当于命令角色。那么从这一点出发来想这个案例中老板发号施令的好处所在,老板只需要说一下命令,然后,具体谁去做,怎么做老板不需要知道的,只需要等待命令的结果就行了。
案例中类结构图
主要代码
接收者角色
public interface Person {
/**
* 劈柴
*/
public void chopping();
/**
* 做饭
*/
public void makeFood();
/**
* 喂马
*/
public void feedHorse();
/**
* 周游世界
*/
public void travelWorld();
}
具体实现类
public class Man implements Person{
@Override
public void chopping() {
System.out.println("Man --- chopping");
}
@Override
public void makeFood() {
System.out.println("Man --- makeFood");
}
@Override
public void feedHorse() {
System.out.println("Man --- feedHorse");
}
@Override
public void travelWorld() {
System.out.println("Man --- travelWorld");
}
}
public class Woman implements Person{
@Override
public void chopping() {
System.out.println("Woman --- chopping");
}
@Override
public void makeFood() {
System.out.println("Woman --- makeFood");
}
@Override
public void feedHorse() {
System.out.println("Woman --- feedHorse");
}
@Override
public void travelWorld() {
System.out.println("Woman --- travelWorld");
}
}
命令角色
public abstract class Command {
protected Man man = new Man();
protected Woman woman = new Woman();
public abstract void executeBossCommand();
}
具体实现类
public class ChoppingCommand extends Command {
@Override
public void executeBossCommand() {
this.man.chopping();
}
}
public class MakeFoodCommand extends Command {
@Override
public void executeBossCommand() {
this.woman.makeFood();
}
}
调用者
public class Boss {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand(){
this.command.executeBossCommand();
}
}
测试类
public class MainTest {
public static void main(String[] args) {
Boss boss = new Boss();
Command command1 = new ChoppingCommand();
boss.setCommand(command1);
boss.executeCommand();
Command command2 = new MakeFoodCommand();
boss.setCommand(command2);
boss.executeCommand();
}
}
模式扩展
情景介绍
假如此时老板很明确的是男的佣人要去劈柴和喂马,而女佣人可以去做饭和旅游,那么这种情况下是不是可以进行扩展呢?
主要代码
代码分析,如果说可以扩展的话,那么其余的部分需要进行变更,也就是在上面例子的基础上只需要扩展子类即可,那么这里我不再列出其类的结构图了,因为基本是一致的。
扩展命令角色为ManCommand和WomanCommand两个具体的实现类。
ManCommand
public class ManCommand extends Command{
/**
* 让男人干点劈柴喂马的事
*/
@Override
public void executeBossCommand() {
this.man.chopping();
this.man.feedHorse();
}
}
WomanCommand
public class WomanCommand extends Command{
/**
* 让女人干点做饭和周游世界的事
*/
@Override
public void executeBossCommand() {
this.woman.makeFood();
this.woman.travelWorld();
}
}
测试类
public class MainTest {
public static void main(String[] args) {
Boss boss = new Boss();
ManCommand manCommand = new ManCommand();
boss.setCommand(manCommand);
boss.executeCommand();
WomanCommand womanCommand = new WomanCommand();
boss.setCommand(womanCommand);
boss.executeCommand();
}
}
从此案例可以看出命令模式的确扩展性很强
,符合开闭原则。
命令模式的优点
1. 解耦:调用者和接受者之间没有关系,调用者在调用的时候,只需要执行命令(Command)中的执行方法就可以了,不需要了解哪个接收者去实际的执行。
2. 可扩展性:Command部分容易扩展,调用者不需要知道有什么改变,维持原有的设计即可完成扩展,从这点上看,符合开闭原则,对修改关闭对于扩展开放。
命令模式的缺点
命令过多的时候,会导致子类过多,系统类臃肿且难于维护,不过可以结合其他的设计模式进行整合,使得优点得到更好的发挥,如结合模板方法模式解决子类过多的问题。
命令模式的适用场景
只要可认为是命令的地方都可以采用命令模式,比如,按钮的点击和拖拽,相当于两个命令,而按钮作为接受者,此时就可以采用命令模式进行设计。
源码下载