前言
命令模式是一种高内聚的模式,使用频率其实也不算太高,本质是对命令进行封装,将发出命令的责任和执行命令的责任分离开。
命令模式将一个或一组命令封装为一个对象,从而能够将函数方法作为参数进行传输,同时还能够解耦客户端和服务端的直接耦合,适用场景有:做简单的请求排队,记录请求日志,以及支持可撤销的操作。
目录
一、定义
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
二、模式原理分析
//1.抽象命令类
public interface Command {
void excute();
}
//2.1 具体命令类
public class Command1 implements Command {
private final Receiver receiver;
public Command1(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void excute() {
receiver.operationA();
}
}
//2.2 具体命令类
public class Command2 implements Command {
private final Receiver receiver;
public Command2(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void excute() {
receiver.operationB();
}
}
//2.3 具体命令类
public class Command3 implements Command {
private final Receiver receiver;
public Command3(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void excute() {
receiver.operationC();
}
}
//3 抽象接收者
public interface Receiver {
void operationA();
void operationB();
void operationC();
}
//4 具体接收者
public class Receiver1 implements Receiver {
@Override
public void operationA() {
System.out.println("操作 A");
}
@Override
public void operationB() {
System.out.println("操作 B");
}
@Override
public void operationC() {
System.out.println("操作 C");
}
}
//5 调用者
public class Invoker {
private final List<Command> commands;
public Invoker() {
commands = new ArrayList<>();
}
public void setCommand(Command command) {
commands.add(command);
}
public void run() {
commands.forEach(Command::execute);
}
}
//6. 场景类
public class Demo {
public static void main(String[] args) {
Receiver receiver1 = new Receiver1();
Invoker invoker = new Invoker();
invoker.setCommand(new Command1(receiver1));
invoker.setCommand(new Command2(receiver1));
invoker.setCommand(new Command3(receiver1));
invoker.run();
}
}
//输出结果
操作 A
操作 B
操作 C
三、使用场景
- 只要是认为有命令的地方就可以采用命令模式
四、优点
-
类间解耦,调用者角色和接收者角色没有依赖关系,调用者只能调用
Command
的execute
-
可扩展性,
Command
的子类可以非常容易地扩展 -
命令模式可以结合责任链模式,实现命令族解析任务,结合模板方法,还可以减少
Command
子类膨胀问题
五、缺点
-
如果有
N
个命令,Command
的子类就有N
个,类膨胀问题严重 -
不同的接收者可能需要实现重复的命令,例如
Markdown
需要实现打开、关闭、保存的命令,而非Markdown
编辑器也需要实现打开、关闭、保存的命令