这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
设计模式
WangScaler: 一个用心创作的作者。
声明:才疏学浅,如有错误,恳请指正。
命令模式
什么是命令模式?
命令模式将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。它就像遥控器一样,我们只需要按遥控器的按键发出命令来实现我们的目标,而我们无需知道命令的执行细节。
我们都玩过收音机(00后可能没见过),假设收音机有2个按键:播放键、倒带键。而二次按播放键就是停止键,二次按倒带键就是停止倒带。
接下来就以收音机为例,讲解我们的命令模式。
首先创建命令的接收者Radio.java,实现命令的四个功能:播放、停止、倒带、停止倒带
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 17:45
*/
public class Radio {
public void play() {
System.out.println("开始播放");
}
public void stop() {
System.out.println("停止播放");
}
public void rewind() {
System.out.println("开始倒带");
}
public void stopRewind() {
System.out.println("停止倒带");
}
}
定义一个抽象命令接口Command.java,这个接口一个是execute执行(第一次按键),一个是undo取消(第二次按键)。
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 17:17
*/
public interface Command {
public void execute();
public void undo();
}
定义具体命令PlayCommand,通过调用接收者Radio,来完成命令的执行。
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 17:36
*/
public class PlayCommand implements Command {
private Radio redio;
public void execute() {
redio.play();
}
public void undo() {
redio.stop();
}
}
RewindCommand.java,通过调用接收者Radio,来完成命令的执行
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 17:38
*/
public class RewindCommand implements Command {
private Radio redio;
public void execute() {
redio.rewind();
}
public void undo() {
redio.stopRewind();
}
}
请求者角色,按键Keypad,它拥有很多命令对象,来实现命令。
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 18:14
*/
public class Keypad {
private Command playCommand;
private Command rewindCommand;
public void setPlayCommand(Command playCommand) {
this.playCommand = playCommand;
}
public void setRewindCommand(Command rewindCommand) {
this.rewindCommand = rewindCommand;
}
public void play() {
playCommand.execute();
}
public void rewind() {
rewindCommand.execute();
}
public void stop() {
playCommand.undo();
}
public void stopRewind() {
rewindCommand.undo();
}
}
main
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/27 18:16
*/
public class Main {
public static void main(String[] args) {
Radio redio = new Radio();
Command playCommand = new PlayCommand(redio);
Command rewindCommand = new RewindCommand(redio);
Keypad keypad = new Keypad();
keypad.setPlayCommand(playCommand);
keypad.setRewindCommand(rewindCommand);
keypad.rewind();
keypad.stopRewind();
keypad.play();
keypad.stop();
}
}
最终我们通过按按键实现了四个功能。这就是命令模式。
在这里我们按键Keypad拥有一系列的命令对象playCommand和rewindCommand,而我们的接收者Radio具体实现了play、stop、rewind、stopRewind四个功能,我们的命令对象通过组合Radio来调用他的功能,对我们请求者来说,只需要按按键来实现我们想要的功能,而无需关心Radio具体怎么实现的这些功能。
这里我们将按键与收音机内核解耦了,如果想扩展新命令,只需在此基础上增加命令即可。
源码中的命令模式
JDK中的Runnable
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Runnable就是相当于我们命令模式中的抽象命令类角色,里面定义了一个执行命令的方法run,相当于我们例子中的execute方法。
我们可以自定义具体命令角色,还是基于上述的例子上写
package com.wangscaler.command;
/**
* @author WangScaler
* @date 2021/7/28 15:17
*/
public class TurnOnThread implements Runnable {
private Radio radio;
public TurnOnThread(Radio radio) {
this.radio = radio;
}
public void run() {
radio.play();
}
}
我们的接收者角色还是上述例子中的Radio。这次Thread可以充当我们的请求者角色,通过start()来实现命令的下发,Thread的源码如下
package java.lang;
public class Thread implements Runnable {
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
}
总结
命令模式的角色
-
- 抽象命令类角色:定义抽象的执行命令的方法execute()。
-
- 具体命令类角色:组合接收者,通过调用接受者的功能来实现命令的执行。
-
- 接收者角色:命令的真正实现者,功能的具体实现。
-
- 请求者角色:具有很多命令对象,通过命令对象完成命令的下发。
缺点:
需要大量的具体命令类。
何时使用:
比如我们的万能遥控器,我们怎么控制空调、电视、电扇呢?我们的空调、电视、电扇都可以提供命令对象API,我们的万能遥控器可以通过这些命令对象,实现对空调、电视、电扇的控制。