定义:将请求封装成对象,这可以让你使用不同的请求、队列,或者日志请求来参数化其它对象。命令模式也可以支持撤销操作。
案例一:将实现自定义接口的任务对象放进自定义执行器里面执行。
1、定义任务接口:该接口为函数式接口(JDK8新特性,接口只有一个抽象方法,可使用Lambda表达式)。
/**
* 函数式接口
* @author z_hh
* @time 2018年7月27日
*/
@FunctionalInterface
public interface MyTask {
/**
* 执行操作
*/
void execute();
}
2、定义任务执行器。
/**
* 自定义执行器
* @author z_hh
* @time 2018年7月27日
*/
public class MyExecutor {
// 阻塞队列,当put或者take时,没有空间或没有元素时,会处于等待状态
private BlockingQueue<MyTask> tasks;
// 真正的执行器
private ExecutorService executor;
// 开关,使用volatile关键字让变量具有可见性
private volatile boolean enable;
public MyExecutor() {
// 初始化一个100容量的有界队列
tasks = new ArrayBlockingQueue<>(100);
}
/**
* 执行器启动
* @throws InterruptedException
*/
public synchronized void run() throws InterruptedException {
// 开始时重新分配一个线程池,因为Executor被shutdown后无法再使用
executor = Executors.newCachedThreadPool();
enable = true;
System.out.println("执行器开启...");
new Thread(() -> {
while (enable) {
MyTask task = null;
try {
task = tasks.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断是否已关闭
if (!executor.isShutdown() && Objects.nonNull(task)) {
executor.execute(task::execute);
}
};
}).start();
}
/**
* 增加执行任务
* @param task
* @throws InterruptedException
*/
public void setTask(MyTask task) throws InterruptedException {
tasks.put(task);
}
/**
* 关闭执行器
*/
public synchronized void stop() {
enable = false;
executor.shutdown();
System.out.println("执行器已关闭...");
}
/**
* 获取执行器是否开启的状态
* @return boolean false已关闭 true已开启
*/
public boolean getEnable() {
return enable;
}
}
3、测试代码。
public class Test {
public static void main(String[] args) throws InterruptedException {
// 创建我的执行器
MyExecutor executor = new MyExecutor();
// 添加任务1
executor.setTask(() -> System.out.println("执行了任务1..."));
sleepSecond(1);// 睡眠
// 启动执行器
executor.run();
sleepSecond(1);// 睡眠
// 添加任务2
executor.setTask(() -> System.out.println("执行了任务2..."));
sleepSecond(1);// 睡眠
// 关闭执行器
executor.stop();
sleepSecond(1);// 睡眠
// 添加任务3
executor.setTask(() -> System.out.println("执行了任务3..."));
// 启动执行器
executor.run();
sleepSecond(1);// 睡眠
// 添加任务3
executor.setTask(() -> System.out.println("执行了任务4..."));
}
// 睡眠指定秒数
private static void sleepSecond(int num) {
try {
Thread.sleep(num * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4、结果。
执行器开启...
执行了任务1...
执行了任务2...
执行器已关闭...
执行器开启...
执行了任务3...
执行了任务4...
案例二:给一个遥控器设置控制家电开关的指令(雷同《Head First设计模式(中文版)的命令模式案例》)。
1、定义指令接口,有执行和撤销的动作。
/**
* 命令接口
* @author z_hh
* @time 2018年7月27日
*/
public interface Command {
/**
* 执行
*/
void execute();
/**
* 撤销
*/
void undo();
}
2、定义两个家电,电灯和电脑。
/**
* 电灯类
* @author z_hh
* @time 2018年7月27日
*/
public class Light {
public void on() {
System.out.println("电灯打开");
}
public void off() {
System.out.println("电灯关闭");
}
}
/**
* 电脑类
* @author z_hh
* @time 2018年7月27日
*/
public class Computer {
public void open() {
System.out.println("打开电脑");
}
public void close() {
System.out.println("关闭电脑");
}
}
3、定义实现指令接口的打开/关闭电灯和电脑的指令类。
/**
* 打开电灯命令
* @author z_hh
* @time 2018年7月27日
*/
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
/**
* 关闭电灯命令
* @author z_hh
* @time 2018年7月27日
*/
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
/**
* 打开电脑命令
* @author z_hh
* @time 2018年7月27日
*/
public class ComputerOpenCommand implements Command {
Computer computer;
public ComputerOpenCommand(Computer computer) {
this.computer = computer;
}
@Override
public void execute() {
computer.open();
}
@Override
public void undo() {
computer.close();
}
}
/**
* 关闭电脑命令
* @author z_hh
* @time 2018年7月27日
*/
public class ComputerCloseCommand implements Command {
Computer computer;
public ComputerCloseCommand(Computer computer) {
this.computer = computer;
}
@Override
public void execute() {
computer.close();
}
@Override
public void undo() {
computer.open();
}
}
4、定义什么都不做的空指令。
/**
* 不做任何操作的命令
* @author z_hh
* @time 2018年7月27日
*/
public class NoCommand implements Command {
@Override
public void execute() {}
@Override
public void undo() {}
}
5、定义遥控器类。
/**
* 遥控器
* @author z_hh
* @time 2018年7月27日
*/
public class RemoteControl {
// 打开类命令
private Command[] onCommands;
// 关闭类命令
private Command[] offCommands;
// 撤销命令
private Command undoCommand;
// 构造器,初始化命令数组,并设置原始命令
public RemoteControl() {
onCommands = new Command[2];
offCommands = new Command[2];
Command noCommand = new NoCommand();
for (int i = 0; i < 2; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
/**
* 设置命令
* @param slot
* @param onCommand
* @param offCommand
*/
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* 打开按钮
* @param slot
*/
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
/**
* 关闭按钮
* @param slot
*/
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
/**
* 撤销按钮
*/
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
6、测试。
/**
* 命令模式
* @author z_hh
* @time 2018年7月27日
*/
public class Test {
public static void main(String[] args) {
// 创建遥控器
RemoteControl remoteControl = new RemoteControl();
// 创建电灯对象,并设置遥控器的1号开关
Light light = new Light();
LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);
remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
// 创建电脑对象,并设置遥控器的2号开关
Computer computer = new Computer();
ComputerOpenCommand computerOpenCommand = new ComputerOpenCommand(computer);
ComputerCloseCommand computerCloseCommand = new ComputerCloseCommand(computer);
remoteControl.setCommand(1, computerOpenCommand, computerCloseCommand);
// 第一组开关
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
// 第二组开关
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
// 回退
remoteControl.undoButtonWasPushed();
}
}
7、结果。
电灯打开
电灯关闭
打开电脑
关闭电脑
打开电脑
8、还可以定义宏命令:将多个指令组成一个命令,比如将打开电灯和打开电脑的指令放在一个遥控器命令上。