命令模式(CommandPattern)
属于行为型模式
的一种,又称为行动(Action)模式或交易(Transaction)模式。将一个请求封装为一个对象,从而达到用不同的请求对客户进行参数化,对于排队请求或请求日志记录,可以提供命令的撤销和恢复功能。
命令模式:对命令的封装,把发送命令和执行命令的责任分割开,分别委派给不同的对象,每一个命令都是一个操作,允许请求方与接收方独立开来,使之请求方不必清楚接收方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
UML结构图
模式结构
exceute()
等方法抽象命令类
的子类,对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方 法时,将调用接收者对象的相关操作(Action)。博主比较喜欢听歌,这里就以 MusicPlayer(音乐播放器)
为案例,一般播放器中都有 播放(play)
, 跳过(skip)
, 停止(stop)
等功能,是一种比较典型的命令模式
UML图如下:
1.定义 Command(抽象命令类)
,只有一个 execute()
用来执行命令
interface Command {
void execute();
}
2.创建不同指令的 ConcreteCommand(具体命令类)
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(调用者)
,接收客户端发送过来的指令
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(接收者)
,执行接收到的指令
class MusicPlayer {
public void play() {
System.out.println("播放...");
}
public void skip() {
System.out.println("跳过...");
}
public void stop() {
System.out.println("停止...");
}
}
5.测试类 MusicPlayerClient
public class MusicPlayerClient {
public static void main(String[] args) {
// 创建 Receiver(接收者)
MusicPlayer musicPlayer = new MusicPlayer();
// Command(抽象命令类)
Command playCommand = new PlayCommand(musicPlayer);
Command skipCommand = new SkipCommand(musicPlayer);
Command stopCommand = new StopCommand(musicPlayer);
// 创建 Invoker(调用者)
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(命令类)
interface MacroCommand extends Command {
void add(Command command);
void remove(Command command);
}
2.创建 MacroMusicCommand
实现 MacroCommand
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.测试类
public class MusicPlayerClient {
public static void main(String[] args) {
// 创建 Receiver(接收者)
MusicPlayer musicPlayer = new MusicPlayer();
// Command(抽象命令类)
Command playCommand = new PlayCommand(musicPlayer);
Command skipCommand = new SkipCommand(musicPlayer);
Command stopCommand = new StopCommand(musicPlayer);
// 创建 Invoker(调用者)
MacroCommand macroCommand = new MacroMusicCommand();
macroCommand.add(playCommand);
macroCommand.add(skipCommand);
macroCommand.add(stopCommand);
// 测试
macroCommand.execute();
}
}
4.运行结果
我们平时使用的 java.lang.Runnable
就是 命令模式
的经典应用
// 命令类 与 具体命令实现类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("关注 battcn 公众号即可免费领取视频");
}
};
// Invoker(调用者) 接收命令
Thread thread = new Thread(runnable);
// 调用 start 命令
thread.start();
// JDK8 简化写法
new Thread(()->System.out.println("关注 battcn 公众号即可免费领取视频")).start();
优点
缺点
适用场景
全文代码:https://gitee.com/battcn/design-pattern/tree/master/Chapter12/battcn-command