本文节选自《设计模式就该这样学》
1 命令模式的 UML 类图
命令模式的 UML 类图如下图所示。
2 应用命令模式重构播放器管制条
如果咱们开发一个播放器,播放器有播放性能、拖动进度条性能、进行播放性能、暂停性能,咱们在操作播放器的时候并不是间接调用播放器的办法,而是通过一个管制条去传播指令给播放器内核,具体传播什么指令,会被封装为一个个按钮。那么每个按钮就相当于对一条命令的封装。用管制条实现了用户发送指令与播放器内核接管指令的解耦。上面来看代码,首先创立播放器内核 GPlayer 类。
public class GPlayer {public void play(){System.out.println("失常播放");
}
public void speed(){System.out.println("拖动进度条");
}
public void stop(){System.out.println("进行播放");
}
public void pause(){System.out.println("暂停播放");
}
}
创立命令接口 IAction 类。
public interface IAction {void execute();
}
而后别离创立操作播放器能够接管的指令,播放指令 PlayAction 类的代码如下。
public class PlayAction implements IAction {
private GPlayer gplayer;
public PlayAction(GPlayer gplayer) {this.gplayer = gplayer;}
public void execute() {gplayer.play();
}
}
暂停指令 PauseAction 类的代码如下。
public class PauseAction implements IAction {
private GPlayer gplayer;
public PauseAction(GPlayer gplayer) {this.gplayer = gplayer;}
public void execute() {gplayer.pause();
}
}
拖动进度条指令 SpeedAction 类的代码如下。
public class SpeedAction implements IAction {
private GPlayer gplayer;
public SpeedAction(GPlayer gplayer) {this.gplayer = gplayer;}
public void execute() {gplayer.speed();
}
}
进行播放指令 StopAction 类的代码如下。
public class StopAction implements IAction {
private GPlayer gplayer;
public StopAction(GPlayer gplayer) {this.gplayer = gplayer;}
public void execute() {gplayer.stop();
}
}
最初创立管制条 Controller 类。
public class Controller {private List<IAction> actions = new ArrayList<IAction>();
public void addAction(IAction action){actions.add(action);
}
public void execute(IAction action){action.execute();
}
public void executes(){for(IAction action : actions){action.execute();
}
actions.clear();}
}
从下面代码来看,管制条能够执行单条命令,也能够批量执行多条命令。上面来看客户端测试代码。
public static void main(String[] args) {GPlayer player = new GPlayer();
Controller controller = new Controller();
controller.execute(new PlayAction(player));
controller.addAction(new PauseAction(player));
controller.addAction(new PlayAction(player));
controller.addAction(new StopAction(player));
controller.addAction(new SpeedAction(player));
controller.executes();}
因为管制条曾经与播放器内核解耦了,当前如果想扩大新命令,只需减少命令即可,管制条的构造毋庸改变。
3 命令模式在 JDK 源码中的利用
首先来看 JDK 中的 Runnable 接口,Runnable 相当于命令的形象,只有是实现了 Runnable 接口的类都被认为是一个线程。
public interface Runnable {public abstract void run();
}
实际上调用线程的 start() 办法之后,就有资格去抢 CPU 资源,而不须要编写取得 CPU 资源的逻辑。而线程抢到 CPU 资源后,就会执行 run() 办法中的内容,用 Runnable 接口把用户申请和 CPU 执行进行解耦。
4 命令模式在 JUnit 源码中的利用
再来看一个大家十分相熟的 junit.framework.Test 接口。
package junit.framework;
public interface Test {public abstract int countTestCases();
public abstract void run(TestResult result);
}
Test 接口中有两个办法,第一个是 countTestCases() 办法,用来统计以后须要执行的测试用例总数。第二个是 run() 办法,用来执行具体的测试逻辑,其参数 TestResult 是用来返回测试后果的。实际上,咱们在平时编写测试用例的时候,只须要实现 Test 接口就被认为是一个测试用例,那么在执行的时候就会被自动识别。通常做法都是继承 TestCase 类,无妨来看一下 TestCase 的源码。
public abstract class TestCase extends Assert implements Test {
...
public void run(TestResult result) {result.run(this);
}
...
}
实际上,TestCase 类也实现了 Test 接口。咱们继承 TestCase 类,相当于也实现了 Test 接口,天然就会被扫描成为一个测试用例。