具有附加参数解析的命令模式

问题描述 投票:-2回答:2

最近我真正专注于设计模式并实施它们来解决不同的问题。今天我正在研究命令模式。

我最终创建了一个界面:

public interface Command {
    public void execute();
}

我有几个具体的实现:

public class PullCommand implements Command {

    public void execute() {
        // logic
    }
}

和:

public class PushCommand implements Command {

    public void execute() {
        // logic
    }
}

还有其他几个命令。

现在..事情是有一个BlockingQueue<Command>运行在不同的线程上使用.take()来检索排队的命令并执行它们(我在下面称之为Executor类)由另一个类通过解析用户输入生成它们使用.queue()。到现在为止还挺好...

关于我的难点在于解析命令(CLI应用程序)。我把所有这些都放在了HashMap中:

private HashMap<String, Command> commands = new HashMap<String, Command>();
commands.put("pull", new PullCommand());
commands.put("push", new PushCommand());
//etc..

当用户输入一个命令时,语法是这样的,有一件事是肯定的,并且“动作”(拉/推)作为第一个参数,所以我总是可以做commands.get(arguments[0])并检查是否为null,如果它是,那么命令无效,如果不是,那么我已成功检索到代表该命令的对象。棘手的部分是还有其他参数也需要解析,并且对于每个命令,解析它的算法是不同的......显然,我可以做的一件事是将arguments[]作为参数放入方法execute()并最终有execute(String[] args)但这意味着我必须将参数的解析放在命令的execute()方法中,我想避免这个原因有以下几点:

  1. Command的执行发生在使用BlockingQueue的不同线程上,它执行单个命令,然后执行另一个命令等。我想在execute()中放入的逻辑只能执行命令本身,而不需要解析或任何例如繁重的任务都会减慢执行速度(我确实意识到解析几个args不会破坏性能那么多......但是我在这里学习结构设计和方法来建立良好的编码习惯和很好的解决方案。这不是完美的任何意思)
  2. 这让我觉得我打破了“命令”模式的一些基本原则。 (即使不是这样,我想想一个更好的解决方法)

很明显,我不能使用具体命令的构造函数,因为HashMap返回已经初始化的对象。接下来要想到的是在对象中使用另一个方法“处理”(process(String[] args))参数并将私有变量设置为解析结果,并且在对命令执行process(String[] args)之前,Producer类会调用此queue()方法,因此解析将结束于Executor类(线程)的OUT和从上面的点1不会是一个问题。

但是还有另外一个问题..如果用户向应用程序输入大量命令,应用程序对参数执行.get(args[0])并检索PullCommand,它使用process(String[] args)并设置私有变量,那么会发生什么情况,因此命令排队到执行程序等级,等待执行。同时..另一个命令由用户输入,再次使用.get(args[0]),它从PullCommand中检索HashMap(但是PullCommand与排队执行的那个相同)并且在命令执行之前将调用process()通过Executor类,它会搞砸私有变量。我们最终会在BlockingQueue中使用2个PullCommands记录,第二个从用户的角度来看是正确的(因为他输入了他想要它做的事情而且它就是这样),但第一个将与第二个相同(因为它是同一个对象)并且它不对应于初始参数。

我想到的另一件事是使用Factory类来实现每个命令的解析并返回相应的Command对象。这意味着,我需要改变使用HashMap的方式,而不是命令我必须使用Factory类:

HashMap<String, CommandFactory> commands = new HashMap<String, CommandFactory>();
commands.put("pull", new CommandFactory("pull"));
commands.put("pull", new CommandFactory("push"));

并且基于传递给Factory的String,它的process()方法将对该命令使用适当的解析,它将返回相应的Command对象,但这意味着该类可能非常大,因为包含对所有命令的解析..

总的来说,这似乎是我唯一的选择,但我非常犹豫,因为从结构的角度来看,我认为我没有很好地解决这个问题。这是处理这种情况的好方法吗?有什么我想念的吗?有什么方法可以重构我的部分代码来缓解它吗?

java parsing design-patterns structure
2个回答
0
投票

你在想这个。命令模式基本上是“保留你需要知道如何做某些事情并在以后做的事情”,所以将东西传递给执行代码是可以的。

这样做:

  • 用户输入String[]
  • 第一个字符串是命令“name”(现在使用它)
  • 其余字符串是命令的参数(如果有的话)
  • 将您的界面更改为public void execute(String[] parameters);
  • 要执行,请将参数传递给命令对象

0
投票

在SO中广泛地提出这样的设计问题通常不是一个好主意。只看到没有近距离请求的downvote有点令人惊讶。

在任何情况下,如果不彻底了解你的问题,很难说什么是“最好的”设计,更不用说我做了什么,我不会称任何东西是最好的。因此,我将坚持使用Builder模式我说的话。

通常,只要构造逻辑太复杂而无法适应构造函数,就会使用构建器模式,或者它必须分为几个阶段。在这种情况下,如果你想要一些极端多样化的命令应该如何取决于动作,那么你会想要一些像这样的构建器:

interface CommandBuilder<T extends Command> {
    void parseArgs(String[] args);
    T build();
}

如果您不打算比当前架构更多地使用这些构建器,那么这里的Generic是可选的,否则在类型中更精确是有益的。 parseArgs负责您所指的那些必要的解析。 build应根据您当前的论点吐出Command的实例。

那么您希望您的调度程序映射如下所示:

HashMap<String, Supplier<? extends CommandBuilder<? extends Command>>> builders = new HashMap<>();
commands.put("pull", () -> new PullBuilder());
commands.put("push", () -> new PushBuilder());
// etc

任何这些构建器都可能具有极其复杂的逻辑,正如您所希望的那样。那你可以做

CommandBuilder builder = builders.get(args[0]).get();
builder.setArgs(args);
queue.add(builder.build());

通过这种方式,您的Command界面可以专注于它应该做的事情。

请注意,在构建builders映射之后,一切都是静态的,并且突变是本地化的。我不完全明白你的担忧是什么,但应该通过这样做来解决。

但是,它可能是一种过度的设计,取决于您想要做什么。

© www.soinside.com 2019 - 2024. All rights reserved.