写点什么

使用命令模式重构播放器控制条

作者:Tom弹架构
  • 2021 年 11 月 22 日
  • 本文字数:2257 字

    阅读完需:约 7 分钟

本文节选自《设计模式就该这样学》

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 接口,自然就会被扫描成为一个测试用例。


关注微信公众号『 Tom 弹架构 』回复“设计模式”可获取完整源码。


【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦


本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom 弹架构 』可获取更多技术干货!

发布于: 2021 年 11 月 22 日阅读数: 9
用户头像

Tom弹架构

关注

不只做一个技术者,更要做一个思考者 2021.10.22 加入

畅销书作者,代表作品: 《Spring 5核心原理与30个类手写实战》 《Netty 4核心原理与手写RPC框架实战》 《设计模式就该这样学》

评论

发布
暂无评论
使用命令模式重构播放器控制条