资讯专栏INFORMATION COLUMN

一起学设计模式 - 命令模式

BakerJ / 1373人阅读

摘要:命令模式属于行为型模式的一种,又称为行动模式或交易模式。结构图模式结构抽象命令类声明了用于执行请求的的等方法具体命令类抽象命令类的子类,对应具体的接收者对象,将接收者对象的动作绑定其中。

命令模式(Command Pattern)属于行为型模式的一种,又称为行动(Action)模式或交易(Transaction)模式。将一个请求封装为一个对象,从而达到用不同的请求对客户进行参数化,对于排队请求或请求日志记录,可以提供命令的撤销和恢复功能。

概述

命令模式:对命令的封装,把发送命令和执行命令的责任分割开,分别委派给不同的对象,每一个命令都是一个操作,允许请求方与接收方独立开来,使之请求方不必清楚接收方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

UML结构图

模式结构

Command(抽象命令类): 声明了用于执行请求的的exceute()等方法

ConcreteCommand(具体命令类): 抽象命令类的子类,对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方

法时,将调用接收者对象的相关操作(Action)。

Invoker(调用者): 调用命令对象执行请求,相关的方法叫做行动方法。

Receiver(接收者): 负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

案例

博主比较喜欢听歌,这里就以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 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.运行结果

JDK中应用

我们平时使用的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

个人QQ:1837307557

battcn开源群(适合新手):391619659

微信公众号:battcn(欢迎调戏)

福利

关注公众号:battcn,回复springboot即可获得 <2017最新spring boot 外卖实战微信公众平台视频教程>

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70657.html

相关文章

  • 一起设计模式 - 备忘录模式

    摘要:备忘录模式常常与命令模式和迭代子模式一同使用。自述历史所谓自述历史模式实际上就是备忘录模式的一个变种。在备忘录模式中,发起人角色负责人角色和备忘录角色都是独立的角色。 备忘录模式(Memento Pattern)属于行为型模式的一种,在不破坏封装特性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态。 概述 备忘录模式又叫做快照模式(...

    roland_reed 评论0 收藏0
  • 一起设计模式 - 工厂模式

    摘要:抽象工厂模式提供了创建一系列相互依赖对象的接口,无需指定具体类抽象工厂模式是围绕着一个超级工厂工作,创造其它的工厂类,也被称为工厂的工厂,这种类型的设计模式是创造性的模式,因为这种模式提供了创建对象的最佳方法之一。 工厂模式是JAVA中最常用的设计模式之一,使用工厂模式后,创建对象的时候不在将创建逻辑暴露给客户端,而是通过实现接口的方式创建对象,这种设计模式也是对象实例化的最佳方式。 ...

    wanglu1209 评论0 收藏0
  • 一起设计模式 - 外观模式

    摘要:适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。 外观模式(Facade Pattern)属于结构型模式的一种,为子系统中的一组接口提供一个统一的入口,它通过引入一个外观角色来简化客户端与子系统之间的交互... 概述 外观模式是一种使用频率非常高的结构型设计模式,当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时...

    zone 评论0 收藏0
  • 一起设计模式 - 建造者模式

    摘要:构造函数参数太多错误的对象状态使用模式在我们的示例中,改造下召唤师类齐天大圣孙悟空上单基石天赋战争雷霆瘟疫之源图奇下路基石天赋战阵热诚皎月女神戴安娜中单建造者模式让我们写的代码更具可读性,可理解为建立复杂的物体。 建造者模式(Builder Pattern)属于创建型模式的一种,将多个简单对象构建成一个复杂的对象,构建过程抽象化,不同实现方法可以构造出不同表现(属性)的对象,还提供了一...

    MockingBird 评论0 收藏0
  • 一起设计模式 - 策略模式

    摘要:策略模式使得算法可以在不影响到客户端的情况下发生变化。下面就以一个示意性的实现讲解策略模式实例的结构。抽象策略角色这是一个抽象角色,通常由一个接口或抽象类实现。优点策略模式提供了管理相关的算法族的办法。 策略模式(Strategy Pattern)属于对象行为型模式的一种,其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以...

    sushi 评论0 收藏0

发表评论

0条评论

BakerJ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<