点击上方“服务端思维”,选择“设为星标”
回复”669“获取独家整理的精选资料集
回复”加群“加入全国服务端高端社群「后端圈」
命令模式
定义
命令封装
每一个命令是一个操作
解耦请求方和接收方
请求方只需要请求执行命令,不需要关心命令是怎么被接受,操作和是否被执行
行为型模式
生活中的场景
遥控器
餐厅点菜
把需求和执行解耦
适用场景
现实语义中具备命令的操作
shell命令
命令菜单
调用者和请求的接受者需要解耦
调用者不需要关心具体命令的执行细节,只需要把对应的命令传递出去就行了
需要抽象出等待执行的行为
撤销
恢复等操作
需要支持命令宏
命令组合操作
shell 中的help命令
通用类图
Receive接收者角色该角色就是干活的角色,命令传递到这里是应该被执行的
Command命令角色需要执行的所有命令都在这里声明。
Invoker调用者角色接收到命令,并执行命令。
具体案例
假如我们自己开发一个播放器,播放器有播放功能、有拖动进度条功能、停止播放功能、暂停功能,我们自己去操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传递指令给播放器内核,那么具体传达什么指令,会被封装为一个ー个的按钮。
那么每个按钮的就相当于是对一条命令的封装。用控制条实现了用户发送指令与播放器內核接收指令的解耦。
播放器定义
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("暂停播放");
}
}
抽象执行的动作定义
public interface IAction {
void execute();
}
播放动作
public class PlayAction implements IAction {
private GPlayer gplayer;
public PlayAction(GPlayer gplayer) {
this.gplayer = gplayer;
}
public void execute() {
gplayer.play();
}
}
暂停动作
public class PauseAction implements IAction {
private GPlayer gplayer;
public PauseAction(GPlayer gplayer) {
this.gplayer = gplayer;
}
public void execute() {
gplayer.pause();
}
}
具体的执行类
这个是具体的执行类,也是我们提供给客户的基础动作入口
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();
}
}
也可以构建一个链通过建造者模式来快速增加Action
源码中的体现
JDK Runnable
Runnable是抽象,具体的实现是用户来实现的
Thread#start
执行后会抢CPU
通过有接口的命令抽象,我们可以有很多的扩展,Run中的逻辑就是一条一条的命令,我们调用者并不需要底层是如何调度CPu的,只需要传入命令也就是Runnable
就行了
Junit Test
public interface Test {
/**
* Counts the number of test cases that will be run by this test.
*/
public abstract int countTestCases();
/**
* Runs a test and collects its result in a TestResult instance.
*/
public abstract void run(TestResult result);
}
junit.framework.TestCase
@SuppressWarnings("deprecation")
public static void assertTrue(String message, boolean condition) {
Assert.assertTrue(message, condition);
}
@SuppressWarnings("deprecation")
public static void assertTrue(boolean condition) {
Assert.assertTrue(condition);
}
同理,单元测试,用户可以不用关心内核,直接调用就行了
总结
优缺点
优点
类间解耦
调用者和接受者之间没有任何依赖关系
调用者实现的时候只需要调用Command抽象类的execute方法就行了
不需要了解到底是谁在执行
可扩展性
Command的子类非常容易扩展,调用者Invoker和高层次的Client不产生严重的代码耦合
命令模式结合其他模式会更优秀
命令模式可以结合责任链模式,实现命令族解析任务
结合模板方法模式,则可以减少Command子类的膨胀问题。
缺点
如果有N个命令,就需要N个Command的子类,类膨胀非常大
使用场景
只要你认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击是一个命令,可以采用命令模式;模拟DOS命令的时候,当然也要采用命令模式;触发-反馈机制的处理等。
— 本文结束 —
关注我,回复 「加群」 加入各种主题讨论群。
对「服务端思维」有期待,请在文末点个在看
喜欢这篇文章,欢迎转发、分享朋友圈
在看点这里