创建命令接口 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 class Test { 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(); } }
运行效果如下:
正常播放 暂停播放 正常播放 停止播放 拖动进度条
由于控制条已经与播放器内核解耦了,以后如果想扩展新命令,只需增加命令即可,控制条
的结构无需改动。
七、命令模式在源码中的体现
由于控制条已经与播放器内核解耦了,以后如果想扩展新命令,只需增加命令即可,控制条
的结构无需改动。
public interface Runnable { public abstract void run(); }
实际上调用线程的start()方法之后,就有资格去抢CPU资源,而不需要我们自己编写获得 CPU资源的逻辑。而线程抢到CPU资源后,就会执行run()方法中的内容,用 Runnable接口 把用户请求和CPU执行进行了解耦。
然 后 ,再看一个大家非常孰悉的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 接口,自然也就会被扫描成为一个测试用例。
八、命令模式的优缺点
优点:
1、通过引入中间件(抽象接口),解耦了命令请求与实现;
2、 扩展性良好,可以很容易地增加新命令;
3、 支持组合命令,支持命令队列;
4、可以在现有命令的基础上,增加额外功能(比如日志记录…,结合装饰器模式更酸爽)。
缺点:
1、 具体命令类可能过多;
2、 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与
实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难(不过这
也是设计模式带来的一个通病,抽象必然会引入额外类型;抽象肯定比紧密难理解)。
十一、迭代器模式与命令模式详解