Command design pattern
命令模式的概念、命令模式的结构、命令模式的优缺点、命令模式的使用场景、命令模式的实现示例、命令模式的源码分析
1、命令模式的概念
命令模式,即将请求封装成一个对象,使发出请求的责任和执行请求的责任分离开。这样两者之间通过命令对象进行沟通,方便讲命令对象进行存储、传递、调用和管理。
2、命令模式的结构
- 抽象命令:定义执行命令的行为。
- 实现者/接受者:命令的实现者或接受者,真正执行命令的对象。任何类都可以成为一个接受者,只要它能够实现命令所要求的功能。
- 具体命令:实现抽象命令,持有接受者的引用,通过委托调用接受者执行命令的方法来实现命令的行为。
- 调用者/请求者:即命令请求,命令发起者,持有命令对象的引用,它可以持有多个命令,即可以一次性发出多个命令。它是客户端真正出发命令并要求命令执行的地方,也就是相当于使用命令对象的入口。
3、命令模式的优缺点
- 优点:
- 降低系统的耦合度。即降低命令请求者与执行者的耦合度。
- 满足开闭原则,增加命令或删除命令不影响其它类,扩展灵活。
- 可实现宏命令,命令模式可与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作,命令模式可与备忘录模式结合,实现命令的撤销和恢复。
- 缺点:
- 导致系统有过多的具体命令类,增加系统复杂度。
4、命令模式的使用场景
- 当需要讲请求者调用者和请求接受者解耦时。
- 当需要在不同的时间指定请求,将请求排队和执行请求。
- 当需要支持命令的撤销操作和恢复操作时。
5、命令模式的实现示例
接受者:
public class Receiver {
/**
* 命令接受者执行命令
* @param name
*/
public void attack(String name) {
System.out.println(name);
}
}
抽象命令:
public interface Command {
/**
* 执行命令
*/
void execute();
}
具体命令:
public class ZedCommand implements Command {
private String name;
private Receiver receiver;
public ZedCommand(String name, Receiver receiver) {
this.name = name;
this.receiver = receiver;
}
@Override
public void execute() {
receiver.attack(this.name);
}
}
调用者:
public class Invoker {
private List<Command> commands;
public Invoker() {
this.commands = new ArrayList<>();
}
public void addCommand(Command command) {
this.commands.add(command);
}
/**
* 发出命令
*/
public void commands() {
for (Command command : this.commands) {
command.execute();
}
}
}
测试:
public class CommandTest {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Receiver receiver = new Receiver();
invoker.addCommand(new ZedCommand("禁奥义·瞬狱影杀阵", receiver));
invoker.addCommand(new ZedCommand("影奥义·分身", receiver));
invoker.addCommand(new ZedCommand("影奥义·鬼斩", receiver));
invoker.addCommand(new ZedCommand("影奥义·诸刃", receiver));
invoker.addCommand(new ZedCommand("影奥义·分身", receiver));
invoker.addCommand(new ZedCommand("影忍法·灭魂劫", receiver));
invoker.addCommand(new ZedCommand("禁奥义·瞬狱影杀阵", receiver));
invoker.commands();
}
}
测试结果:
禁奥义·瞬狱影杀阵
影奥义·分身
影奥义·鬼斩
影奥义·诸刃
影奥义·分身
影忍法·灭魂劫
禁奥义·瞬狱影杀阵
6、命令模式的源码分析
jdk 中的 Runnable 接口的设计就是一个典型的命令模式。Runnable 接口担当命令角色,Thread 则是调用者,start 方法就是命令执行方法。
// 抽象命令
public interface Runnable {
// 执行命令
public abstract void run();
}
public class Thread implements Runnable {
// 持有命令的引用
private Runnable target;
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
}
通过源码可以发现,其在发出命令调用时会调用一个本地方法 start0(),开启一个县城。而接受者是对外开放的,即程序员可以自己定义接受者。