23种设计模式之命令模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
命令模式是将某个产品的相关操作抽象成命令,由命令连接调用者和实现者,使命令的调用者与命令的实现者分离。
特点
- 命令模式是一种行为型模式
通用类图
命令模式包含以下主要角色:
-
Command
抽象命令角色
声明所有执行的命令,拥有执行命令的抽象方法 execute()。
-
ConcreteCommand
具体命令角色
是抽象命令类的具体实现类,组合接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
-
Receiver
抽象接收者角色
定义接收者公共的方法
-
ConcreteReceiver
具体接收者角色
完成执行命令功能的相关操作,是具体命令对象业务的真正实现者。
-
Invoker
调用者角色
接收命令,并执行命令,它聚合命令对象,并通过访问命令对象来执行相关请求。
优点
-
类间解耦
降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦(即调用者角色与接收者角色之间没有任何依赖关系)。
-
可扩展性
增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类(调用者Invoker和高层次的模块Client),它满足“开闭原则”,对扩展比较灵活。
-
宏命令
命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
-
命令模式结合其他模式会更优秀
方便实现 Undo 和 Redo 操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复。
命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。
缺点
- 可能产生大量具体命令类。如果有N个命令,机会产生N个命令类。因为对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
应用场景
- 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
- 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
- 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
二、命令模式
需求:
假设有一个计算器界面,计算器上四个按键,分别是+、-、*、\,并且可以进行整型和浮点型数字的计算,使用命令模式实现该计算器。
抽象命令类(Command):
package command;
public abstract class Command {
// 定义一个子类的全局共享变量
protected final Receiver receiver;
// 实现类必须定义一个接收者
public Command(Receiver receiver) {
this.receiver = receiver;
}
// 每个命令类都必须有一个执行命令操作的方法
public abstract void execute();
}
具体命令类(ConcreteCommand):
package command;
public class AddCommand extends Command {
// 声明自己的默认接收者
public AddCommand() {
super(new CalculateIntegerReceiver());
}
// 设置新的接收者
public AddCommand(Receiver receiver) {
super(receiver);
}
// 每个具体的命令都必须实现一个命令操作
@Override
public void execute() {
// 业务处理
receiver.add();
}
}
package command;
public class SubCommand extends Command {
// 声明自己的默认接收者
public SubCommand() {
super(new CalculateIntegerReceiver());
}
// 设置新的接收者
public SubCommand(Receiver receiver) {
super(receiver);
}
// 每个具体的命令都必须实现一个命令操作
@Override
public void execute() {
// 业务处理
receiver.sub();
}
}
package command;
public class MultiCommand extends Command {
// 声明自己的默认接收者
public MultiCommand() {
super(new CalculateIntegerReceiver());
}
// 设置新的接收者
public MultiCommand(Receiver receiver) {
super(receiver);
}
// 每个具体的命令都必须实现一个命令操作
@Override
public void execute() {
// 业务处理
receiver.multi();
}
}
package command;
public class DivCommand extends Command {
// 声明自己的默认接收者
public DivCommand() {
super(new CalculateIntegerReceiver());
}
// 设置新的接收者
public DivCommand(Receiver receiver) {
super(receiver);
}
// 每个具体的命令都必须实现一个命令操作
@Override
public void execute() {
// 业务处理
receiver.div();
}
}
抽象接收者类(Receiver):
package command;
public interface Receiver {
// 定义每个接收者都必须完成的业务
public void add();
public void sub();
public void multi();
public void div();
}
具体接收者类(ConcreteReceiver):
package command;
public class CalculateFloatReceiver implements Receiver {
@Override
public void add() {
System.out.println("执行浮点数加法功能");
}
@Override
public void sub() {
System.out.println("执行浮点数减法功能");
}
@Override
public void multi() {
System.out.println("执行浮点数乘法功能");
}
@Override
public void div() {
System.out.println("执行浮点数除法功能");
}
}
package command;
public class CalculateIntegerReceiver implements Receiver {
@Override
public void add() {
System.out.println("执行整型加法功能");
}
@Override
public void sub() {
System.out.println("执行整型减法功能");
}
@Override
public void multi() {
System.out.println("执行整型乘法功能");
}
@Override
public void div() {
System.out.println("执行整型除法功能");
}
}
调用者类(Invoker):
package command;
public class Invoker {
// 命令
private Command command;
// 客户端发起命令
public Invoker(Command command) {
this.command = command;
}
// 设置命令
public void setCommand(Command command) {
this.command = command;
}
// 执行命令
public void operate() {
System.out.println("调用者执行命令command...");
command.execute();
}
}
Client:
package command;
public class Client {
public static void main(String[] args) {
// 默认使用 CalculateIntegerReceiver()进行命令处理
Command commandAdd = new AddCommand();
Command commandDiv = new DivCommand();
// 设置使用 CalculateFloatReceiver()进行命令处理
Command commandSub = new SubCommand(new CalculateFloatReceiver());
Command commandMulti = new MultiCommand(new CalculateFloatReceiver());
// 声明调用者Invoker
Invoker invoker = new Invoker(commandAdd);
// 执行命令
invoker.operate();
// 把命令交给调用者去执行
invoker.setCommand(commandSub);
// 执行命令
invoker.operate();
/**
* 输出结果:
* 调用者执行命令command...
* 执行整型加法功能
* 调用者执行命令command...
* 执行浮点数减法功能
*/
}
}
三、总结
命令模式将调用者和实现者进行解耦,减少高层模块(Client类)对低层模块(Receiver类)的依赖关系,提高系统整体的稳定性。