为平滑的cmd线arg处理选择正确的设计模式

时间:2017-07-24 17:33:28

标签: java factory

我的情况如下。

我有一个抽象的Command类,它有一个exec方法。我有一系列具体的命令类来扩展这个抽象类。

我在上下文类中有一个CommandFactory,它根据我解析并发送到工厂的cmdline args创建并返回一个合适的命令。 (这里没问题,我能解析cmdline args就好了。)

然而,在命令工厂中,我有一个if-else的大型列表,如果不是这个

 public Command getCmd(String cmdType){
  if(cmdType == null){
     return null;
  }     
  if(cmdType.equalsIgnoreCase("CLEAN")){
     return new CleanCmd();

  } else if(cmdType.equalsIgnoreCase("KILL")){
     return new KillCmd();

  } else if(cmdType.equalsIgnoreCase("START")){
     return new StartCmd();
  }

  return null;
} 

注意:输入参数是一组标志和参数,对于此问题的范围而言过于复杂。您可以将equalsIgnoreCase视为更复杂的东西。

但是我认为这个if else构造有点难看。我想用更优雅的东西取代它。如果我错了,还要纠正我当前的范例也违反了开闭原则,因为每次我添加一个新命令我都会修改工厂吗?

3 个答案:

答案 0 :(得分:1)

如果可能,我建议使用两个概念:

命令模式,如此answer中所述,并使用一些依赖注入框架,如spring或guice。

有了这两个想法,您的代码可能是这样的:

class CommandFactory {
    // Using spring.
    // This will inject all the classes implementing Command.
    @Autowired
    List<Command> commandList;       

    // Mapping the command name to the implementation  
    Map<String, Command> commandMap;         

    // Initializing the command map
    @PostConstruct
    public void buildMap() {
        for (Command command : commandList) {
            commandMap.put(command.getType(), command);
        }
    }

    public Command getCmd(String cmdType) { 
        return commandMap.get(cmdType);
    }
}

使用此配置,CommandFactory类遵循Open Close Principle。无需修改即可添加新命令,但仍可按需接受新命令。

答案 1 :(得分:0)

做最简单的事情是没有错的,直到它不能满足你的需要为止。

一种方法是你可以完成你已经完成的工作,但稍微更整洁一点,就是使用基于字符串的switch语句:

switch(cmdType) {
    case "KILL":
       return new KillCommand();
    case "CLEAN":
       return new CleanCommand();
    default:
       throw new UnknownCommandException("Unknown command: " + cmdType);
}

我注意到您对null的特殊处理。我建议你尽量避免在任何地方返回null。如果你在整个过程中这样做,那么很多时候你也不需要检查空值。

如果您的需求变得更加复杂,请考虑使用Map<String,Command>

private static Map<String,Command> commandMap = new HashMap<>();
map.put("CLEAN", new CleanCommand());
map.put("KILL", new KillCommand());

...等,然后:

public Command getCommand(String cmdString) {
    return commandMap.get(cmdString.toUpperCase());
}

请注意,每次返回相同的实例 - 如果您可以确保命令类本身是不可变且线程安全的,这通常是一件好事。

如果您每次都需要一个新实例,则可以选择:

  • Map<Class<? extends Command>>并使用map.get(s).newInstance()
  • Map<Supplier<? extends Command>>并使用map.get(s).get()
  • Map<SpecificCommandFactory>并使用SpecificCommandFactory中的方法(如果您尚未定义,我会称之为CommandFactory

使用此模式意味着您可以在需要时准备好其他许多技术:

  • 使用框架(Spring,Guice等)使用依赖注入创建地图
  • 在初始时添加/删除命令(例如从配置文件中)
  • 在运行时添加/删除命令

答案 2 :(得分:-1)

我建议使用原型设计模式代替您正在使用的命令静态工厂(if-then ... ladder)。由于要实例化的可能命令本质上是有限的,因此原型是生成命令的最佳模式。原型模式将保留预先填充的命令库,当客户端部分要求新命令时,它将从存储库获取所需的命令原型并提供克隆作为具体命令。