前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >命令模式浅析

命令模式浅析

作者头像
孟君
发布2020-06-19 16:18:29
4150
发布2020-06-19 16:18:29
举报
文章被收录于专栏:孟君的编程札记

说起命令模式,第一个引入脑海的是Java的GUI图形化编程,其中就采用了命令模式处理事件。在这个事件处理模型里面,命令对象实现AWT的Listener接口,相当于命令接口。为把一个命令对象与一个AWT的构件连接起来,需要把它登记成一个事件的Listener。构件只认识Listener接口,而不在乎接口是怎么实现的。我们编写一个俄罗斯方块游戏,菜单栏等的事件如下:

代码语言:javascript
复制
JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);

    JMenu setMenu = new JMenu("Set");
    JMenu helpMenu = new JMenu("Help");

    setMenu.setMnemonic('s');
    setMenu.setMnemonic('H');

    menuBar.add(setMenu);
    menuBar.add(helpMenu);

    setMenu.add(startMI);
    setMenu.add(pauseMI);
    setMenu.addSeparator();

    setMenu.add(loadMI);
    setMenu.add(saveMI);
    setMenu.add(recordMI);

    setMenu.addSeparator();
    setMenu.add(speedMenu);
    setMenu.addSeparator();
    setMenu.add(exitMI);

    ButtonGroup group = new ButtonGroup();
    group.add(speedMI1);
    group.add(speedMI2);
    group.add(speedMI3);
    group.add(speedMI4);
    group.add(speedMI5);

    speedMenu.add(speedMI1);
    speedMenu.add(speedMI2);
    speedMenu.add(speedMI3);
    speedMenu.add(speedMI4);
    speedMenu.add(speedMI5);

    startMI.addActionListener(new StartAction());
    pauseMI.addActionListener(new PauseAction());
    loadMI.addActionListener(new LoadAction());
    saveMI.addActionListener(new SaveAction());
    recordMI.addActionListener(new RecordAction());
    exitMI.addActionListener(new ExitAction());
    speedMI1.addActionListener(new SpeedAction());
    speedMI2.addActionListener(new SpeedAction());
    speedMI3.addActionListener(new SpeedAction());
    speedMI4.addActionListener(new SpeedAction());
    speedMI5.addActionListener(new SpeedAction());

    helpMenu.add(aboutMI);
    aboutMI.addActionListener(new AboutAction());

具体事件实现如:

代码语言:javascript
复制
  ... ...

  private class ExitAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      int result = JOptionPane.showConfirmDialog(RussiaGameFrame.this,
          "Are you sure quit?", "俄罗斯方块", JOptionPane.YES_NO_OPTION);
      if (result == JOptionPane.YES_OPTION) {
        System.exit(0);
      }
    }
  }

  private class AboutAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      String string = "说明:\n1.按左键向左移动\n" + "2.按右键向右移动\n" + "3.按向上键翻滚\n"
          + "4.按向下键加速下降\n" + "5.按空格键下降到最底部";
      JOptionPane.showMessageDialog(RussiaGameFrame.this, string);
    }
  }
... ... 

接下来,我们来看一下命令模式的一些知识吧。

一. 命令模式的基本介绍

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

结构

命令模式的基本结构如下:

这里涉及到的参与者有如下几种:

  • 命令(Command)角色
    • 声明了一个给所有具体命令类的抽象接口。这是一个抽象角色,通常由一个Java接口或者抽象类实现。
  • 具体命令(ConcreteCommand)角色
    • 定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者相应的操作。execute()方法通常叫做执行方法。
  • 请求者(Invoker)角色
    • 负责调用命令对象的执行请求,相关的方法叫做行动方法。
  • 接收者(Receiver)角色
    • 负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
  • 客户(Command)角色
    • 创建一个具体命令(ConcreteCommand)对象并确定其接收者。

参与者如何协作?

1、Client创建一个ConcreteCommand对象并指定它的Receiver对象

2、某Invoker对象存储该ConcreteCommand

3、该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令。

ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。

二. 命令模式的示例

接下来以一个空调遥控器对空调进行打开、调温、关闭操作,来说明一下命令模式。

  • 抽象命令Command
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public interface Command {

  void execute();
}
  • 具体命令Command

打开命令

代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class TurnOnCommand implements Command {

  private Receiver receiver;

  public TurnOnCommand(Receiver receiver) {
    this.receiver = receiver;
  }

  @Override
  public void execute() {
    receiver.turnOn();
  }

}

升温命令

代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class IncreaseTempCommand implements Command {

  private Receiver receiver;

  public IncreaseTempCommand(Receiver receiver) {
    this.receiver = receiver;
  }

  @Override
  public void execute() {
    receiver.increaseTemp();
  }

}

降温命令

代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class DescTempCommand implements Command {

  private Receiver receiver;

  public DescTempCommand(Receiver receiver) {
    this.receiver = receiver;
  }

  @Override
  public void execute() {
    receiver.descTemp();;
  }

}

关闭命令

代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class TurnOffCommand implements Command {

  private Receiver receiver;

  public TurnOffCommand(Receiver receiver) {
    this.receiver = receiver;
  }

  @Override
  public void execute() {
    receiver.turnOff();
  }


}
  • 请求者
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class AirConditionRemoteControl {

  private Command command;

  public AirConditionRemoteControl(Command command) {
    this.command = command;
  }

  public void click() {
    command.execute();
  }

  /**
   * @param command the command to set
   */
  public void setCommand(Command command) {
    this.command = command;
  }

}
  • 接收者
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public interface Receiver {

  void turnOn();

  void turnOff();

  void increaseTemp();

  void descTemp();


}
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class AirConditionReceiver implements  Receiver{

  @Override
  public void turnOn() {
    System.out.println("打开空调");
  }

  @Override
  public void turnOff() {
    System.out.println("关闭空调");
  }

  @Override
  public void increaseTemp() {
    System.out.println("空调温度升高一度");

  }

  @Override
  public void descTemp() {
    System.out.println("空调温度降低一度");
  }

}
  • 客户端
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.command;

public class Client {

  public static void main(String[] args) {

    Receiver receiver = new AirConditionReceiver();
    TurnOnCommand turnOnCommand = new TurnOnCommand(receiver);
    //1、模拟点击打开按钮
    AirConditionRemoteControl invoker = new AirConditionRemoteControl(turnOnCommand);
    invoker.click();

    //2、模拟点击升温按钮
    invoker.setCommand(new IncreaseTempCommand(receiver));
    invoker.click();
    invoker.click();

    //3、模拟点击降溫按钮
    invoker.setCommand(new DescTempCommand(receiver));
    invoker.click();

    //4、模拟点击关闭按钮
    invoker.setCommand(new TurnOffCommand(receiver));
    invoker.click();

  }
}

输出结果:

代码语言:javascript
复制
打开空调
空调温度升高一度
空调温度升高一度
空调温度降低一度
关闭空调就这样,一个简单的命令模式的示例就完成了。

三. 小结

命令模式的优缺点:

优点:

(1):命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

(2):你可以把命令对象聚合在一起,合成为合成命令。

(3):由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易

缺点:

(1):使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个命令类,这会使命令模式在这样的系统变得不实际。

参考

[1]. 阎宏. Java与模式.电子工业出版社

[2]. Erich Gamma. 设计模式-可复用面向对象软件的基础. 机械工业出版社.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 命令模式的基本介绍
  • 二. 命令模式的示例
  • 三. 小结
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档