基于字符串的对象创建

时间:2019-03-10 11:59:00

标签: java oop design-patterns chatbot

假设我正在编写一个聊天机器人(discord,电报等)。该机器人可以处理聊天命令(例如!join告诉它加入服务器上的语音通道)。

所以在我的代码中,我必须解析该命令,并且会有类似的东西

String userMessage = getTheMessageTextSomehow();
// Do something with the  message.

我希望每个命令都有一个Command类,并且每个命令都将实现execute()方法。

我的问题是:创建这些命令对象的最佳实践是什么?

最简单的方法是拥有一个大型CommandFactory或类似的类,

if(message.equals(JOIN_MESSAGE) {
    return new JoinCommand();
} 
if(message.equals(LEAVE_MESSAGE){
    return new LeaveCommand();
}
//etc...

对我来说,这似乎是一种不好的做法,并且代码有异味。 有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

您可能希望依靠Map个中的Command个。
我将明确说明,对于此用例,使用FunctionSupplier或任何标准的功能接口都不是惯用的。避免它。

我们可以先构建一个Command界面

interface Command {
   Result execute(); 
}

或者如果您需要接受参数

interface Command {
   Result execute(final Input input); 
}

哪些将具有所需的实现

class JoinCommand implements Command { ... }
class LeaveCommand implements Command { ... }
class NoopCommand implements Command { ... }

以此类推。
现在,您需要将这些定义存储在key(命令)-value(实现)数据结构中。 Map就是完美的选择。

因为您的命令定义将是String,然后

static final Map<String, Command> COMMANDS = new HashMap<>(8);

static {
   COMMANDS.put("join", new JoinCommand());
   COMMANDS.put("leave", new LeaveCommand());
   // And so on
}

用法很简单

final String userMessage = getTheMessageTextSomehow();
final String commandStr = extractCommand(userMessage);
final Command command = COMMANDS.getOrDefault(commandStr, NOOP_COMMAND);
command.execute();

或者如果您必须接受参数

command.execute(yourInput);

您还会注意到我使用了NOOP_COMMAND,这只是Command 的无操作实现,以避免处理null 。可能合适,也可能不合适。


如果您在Java 9+上,也可以使用以下方式创建Map

Map.of(
   "join", new JoinCommand(), 
   "leave", new LeaveCommand(),
   // And so on.
)

答案 1 :(得分:0)

通常,它是通过映射实现的。使用简单的Map来实现这一点将更加清晰和可读。

例如:

Map<String, Command> strategies = new HashMap<String, Command>(){{
  put(JOIN_MESSAGE, new JoinCommand());
  put(LEAVE_MESSAGE, new LeaveCommand());
}};

及其用法:

Command command = strategies.get(messageType);

此外,如果需要根据某些参数构造命令,则可以从Java 8开始定义创建策略(工厂)。

Map<String, Function<String, Command>> strategies = new HashMap<String, Command>(){{
  put(JOIN_MESSAGE,  param -> new JoinCommand(param));   // or JoinCommand::new
  put(LEAVE_MESSAGE, param -> new LeaveCommand(param));  // or LeaveCommand::new
}};

及其用法:

Command command = strategies.get(messageType);
command.process(param);

答案 2 :(得分:0)

您好,您可以尝试使用Switch Case语句,这很容易理解,将来如果您有任何更改,也很容易更新代码。

switch(message)
    {
       case JOIN_MESSAGE:
            return new JoinCommand();
            break;

       case LEAVE_MESSAGE:
            return new LeaveCommand();
            break;
    }