【问题标题】:Where to Process User Input in the Command Pattern在命令模式中处理用户输入的位置
【发布时间】:2015-02-24 04:13:46
【问题描述】:

我目前正在客户端类中处理用户输入,但是我觉得通过执行命令对象的执行方法,我将增加重用能力,因为只需实例化命令对象即可使用其功能,而不是依赖于客户端的实现。

因此,命令对象可能如下所示:

public class CommandA implements Command {
    ReceiverA receiverA;

    public CommandA(RecieverA receiverA) {
        this.receiverA = receiverA;
    }

    @Override
    public void execute() {
        Scanner scanner = new Scanner(System.in);

        String x = scanner.nextLine();

        receiverA.methodA(x);

    }
}

我发现缺少示例,因此我想知道这是否是一种“好”做法,因为在示例中我看到命令对象仅使用接收器的方法。

【问题讨论】:

  • 您需要稍微澄清一下您的问题。您的第一段的第二句话中似乎缺少一些单词,并且您没有在示例中向我们展示“执行”方法。

标签: design-patterns command-pattern


【解决方案1】:

希望用户输入足够直接,以便用于命令行解析的库或使用 BNF 语法生成的简单解析器将无需在客户端中放置一堆用户输入识别逻辑。

命令模式的优势在于允许客户端延迟执行一个命令或一系列命令,并允许客户端将命令传递给它选择的调用者。

以一些公开 CLI 并进行一些数据操作的系统为例。假设您要对一些用户操作进行建模:“全部保存”和“打印样本”。这些命令中的每一个都可能缺少用户需要提供的一些必需数据。他们可能还会有一些其他的默认选项。您将使用命令行解析来分离通过命令行提供的用户输入,然后将解析的部分包装或提取到SaveAllPrintSample 命令实例中。粘合逻辑只会使用正确的命令类型。您的代码将类似于:

String[] userInput = ...
Parsed parsedInput = parser.parse(userInput)
Command command;
switch (parsedInput.getCommandName()) {
  case 'save-all':
    command = new SaveAll(parsedInput);
    break;
  ...
}

如果你使用正确的工具,你甚至可以放弃这个胶水代码;以 Java/Scala 为例,请参见 JCommander

在您分离了拆分用户输入的关注点后,您的命令类将获得 已解析 输入,例如:

public class SaveAll implements Command {
  private String targetName;
  private char separator = ','

  private DataSet dataset;
  private Filesystem filesystem;

  public SaveAll(Parsed input) {
    targetName = input.getTargetName();
    if (input.getSeparator() != null) {
      separator = input.getSeparator();
    }
  }

  @Injected
  public void setDataSet(...) { ... }

  @Injected
  public void setFilesystem(...) { ... }

  public void execute() {
    try (Datafile file = filesystem.openForWrite(targetName)) {
      for (Row row : dataset.rows()) {
        file.writeRow(row, separator);
      } // rows loop
    } // auto-resource
  } // end execute()
}

最后,在本例中,客户端(向用户公开 CLI 的工具)可以允许用户连接到各种系统。在下面,每个系统都有一个调用程序。因此,该命令将被移交给适当的调用者。 (并且该调用者很可能会序列化或排队命令。)

【讨论】:

  • 很高兴您在示例中使用了具体命令。但是,如果输入包含错误会发生什么,例如,targetName 是无效文件并导致执行异常?在调用执行之前,不是更健壮的设计会检查有效数据吗?
【解决方案2】:

在 GoF 中使用初始模式定义的 UML:

我认为谁处理输入?的答案相当于Invoker 类(或调用它的类)。

这绝对不是 ConcreteCommands,因为其目的是最终能够撤消或重做命令。如果依赖于解析 execute() 方法中的输入,Re-do(对 execute() 的连续调用)将不起作用。

一个命令对象需要最少的信息来完成它的工作。这意味着它必须知道Receiver 对象(每个命令可能不同)以及命令的一些参数。我根本不会在ConcreteCommand 内部处理输入(即使它被解析)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 2020-12-07
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多