命令模式(Command Pattern)
属于行为型模式
的一种,又称为行动(Action)模式或交易(Transaction)模式。将一个请求封装为一个对象,从而达到用不同的请求对客户进行参数化,对于排队请求或请求日志记录,可以提供命令的撤销和恢复功能。
概述
命令模式:对命令的封装,把发送命令和执行命令的责任分割开,分别委派给不同的对象,每一个命令都是一个操作,允许请求方与接收方独立开来,使之请求方不必清楚接收方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
UML结构图

模式结构
- Command(抽象命令类): 声明了用于执行请求的的
exceute()
等方法
- ConcreteCommand(具体命令类):
抽象命令类
的子类,对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方
法时,将调用接收者对象的相关操作(Action)。
- Invoker(调用者): 调用命令对象执行请求,相关的方法叫做行动方法。
- Receiver(接收者): 负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
案例
博主比较喜欢听歌,这里就以MusicPlayer(音乐播放器)
为案例,一般播放器中都有播放(play)
,跳过(skip)
,停止(stop)
等功能,是一种比较典型的命令模式
UML图如下:

1.定义Command(抽象命令类)
,只有一个execute()
用来执行命令
1 2 3
| interface Command { void execute(); }
|
2.创建不同指令的ConcreteCommand(具体命令类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class PlayCommand implements Command {
private MusicPlayer musicPlayer;
public PlayCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; }
@Override public void execute() { musicPlayer.play(); } }
class SkipCommand implements Command {
private MusicPlayer musicPlayer;
public SkipCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; }
@Override public void execute() { musicPlayer.skip(); } }
class StopCommand implements Command {
private MusicPlayer musicPlayer;
public StopCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; }
@Override public void execute() { musicPlayer.stop(); } }
|
3.MusicInvoker(调用者)
,接收客户端发送过来的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class MusicInvoker {
private Command playCommand; private Command skipCommand; private Command stopCommand;
public void setPlayCommand(Command playCommand) { this.playCommand = playCommand; }
public void setSkipCommand(Command skipCommand) { this.skipCommand = skipCommand; }
public void setStopCommand(Command stopCommand) { this.stopCommand = stopCommand; }
public void play() { playCommand.execute(); }
public void skip() { skipCommand.execute(); }
public void stop() { stopCommand.execute(); } }
|
4.MusicPlayer(接收者)
,执行接收到的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MusicPlayer {
public void play() { System.out.println("播放..."); }
public void skip() { System.out.println("跳过..."); }
public void stop() { System.out.println("停止..."); } }
|
5.测试类MusicPlayerClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MusicPlayerClient { public static void main(String[] args) { MusicPlayer musicPlayer = new MusicPlayer(); Command playCommand = new PlayCommand(musicPlayer); Command skipCommand = new SkipCommand(musicPlayer); Command stopCommand = new StopCommand(musicPlayer); MusicInvoker invoker = new MusicInvoker(); invoker.setPlayCommand(playCommand); invoker.setSkipCommand(skipCommand); invoker.setStopCommand(stopCommand); invoker.play(); invoker.skip(); invoker.stop(); invoker.play(); invoker.stop(); } }
|
6.运行结果

宏命令
宏命令: 又称为组合命令,组合多个命令,它是命令模式和组合模式联用的产物;
假设MusicPlayer(音乐播放器)
有一个记录功能,可以把每一个命令记录下来,在需要的时候又可以将历史记录的命令在执行一遍,这就是所谓的宏命令集功能。
UML图如下:

1.定义MacroCommand(宏命令类)
,继承基础Command(命令类)
1 2 3 4 5 6
| interface MacroCommand extends Command {
void add(Command command);
void remove(Command command); }
|
2.创建MacroMusicCommand
实现MacroCommand
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class MacroMusicCommand implements MacroCommand {
private static final List<Command> COMMANDS = new ArrayList<>();
@Override public void execute() { System.out.println("==========回放开始=========="); COMMANDS.forEach(Command::execute); System.out.println("==========回放结束=========="); }
@Override public void add(Command command) { COMMANDS.add(command); }
@Override public void remove(Command command) { COMMANDS.remove(command); } }
|
3.测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class MusicPlayerClient { public static void main(String[] args) { MusicPlayer musicPlayer = new MusicPlayer(); Command playCommand = new PlayCommand(musicPlayer); Command skipCommand = new SkipCommand(musicPlayer); Command stopCommand = new StopCommand(musicPlayer); MacroCommand macroCommand = new MacroMusicCommand(); macroCommand.add(playCommand); macroCommand.add(skipCommand); macroCommand.add(stopCommand); macroCommand.execute(); } }
|
4.运行结果

JDK中应用
我们平时使用的java.lang.Runnable
就是命令模式
的经典应用
1 2 3 4 5 6 7 8 9 10 11 12 13
| Runnable runnable = new Runnable() { @Override public void run() { System.out.println("关注 battcn 公众号即可免费领取视频"); } };
Thread thread = new Thread(runnable);
thread.start();
new Thread(()->System.out.println("关注 battcn 公众号即可免费领取视频")).start();
|
总结
优点
- 将行为调用者和各种行为分隔开,降低程序的耦合,便于程序扩展;
- 将行为的具体实现封装起来,客户端无需关心行为的具体实现;
- 为多种行为提供统一的调用入口,便于程序对行为的管理和控制;
缺点
- 使用命令模式,不论命令简单还是复杂,都需要写一个命令类来封装,滥用命令模式会导致系统出现过多的具体命令类;
适用场景
- 希望将行为请求者和行为实现者解耦,不直接打交道;
- 希望分离掉行为请求者一部分的责任,行为请求者只需要将命令发给调用者,不再主动的去让行为实现者产生行为,符合单一职责原则;
- 希望可以控制执行的命令列表,方便记录,撤销/重做以及事务等功能;
- 希望可以将请求组合使用,即支持宏命令;
说点什么
全文代码:https://gitee.com/battcn/design-pattern/tree/master/Chapter12/battcn-command
- 个人QQ:1837307557
- battcn开源群(适合新手):391619659
微信公众号:battcn
(欢迎调戏)
福利
关注公众号:battcn
,回复springboot
即可获得 <Spring Boot从入门到实战 基础实战系列教程全集>
与 <2017最新spring boot 外卖实战微信公众平台视频教程>