这段代码有些令人非常不满意:
/* Given a command string in which the first 8 characters are the command name padded on the right with whitespace, construct the appropriate kind of Command object. */ public class CommandFactory { public Command getCommand(String cmd) { cmdName = cmd.subString(0,8).trim(); if(cmdName.equals("START")) { return new StartCommand(cmd); } if(cmdName.equals("END")) { return new EndCommand(cmd); } // ... more commands in more if blocks here // else it's a bad command. return new InvalidCommand(cmd); } }
我对多个出口点没有悔改 - 结构清晰.但我对一系列几乎相同的if语句并不满意.我考虑过将一个字符串映射到命令:
commandMap = new HashMap(); commandMap.put("START",StartCommand.class); // ... etc.
...然后使用Reflection从Map中查找相应类的实例.然而,虽然在概念上优雅,但这涉及相当多的反射代码,任何继承此代码的人都可能不会欣赏 - 尽管这些成本可能会被优势所抵消.将命令值硬编码到commandMap中的所有行几乎都与if块一样糟糕.
更好的是,如果工厂的构造函数可以扫描类路径以查找Command的子类,查询它们的String表示,并自动将它们添加到其库中.
那么 - 我该如何重构呢?
我想有些框架可以免费提供给我这种东西.让我们假设我无法将这些东西迁移到这样的框架中.
以下代码怎么样:
public enum CommandFactory { START { @Override Command create(String cmd) { return new StartCommand(cmd); } }, END { @Override Command create(String cmd) { return new EndCommand(cmd); } }; abstract Command create(String cmd); public static Command getCommand(String cmd) { String cmdName = cmd.substring(0, 8).trim(); CommandFactory factory; try { factory = valueOf(cmdName); } catch (IllegalArgumentException e) { return new InvalidCommand(cmd); } return factory.create(cmd); } }
该valueOf(String)
枚举是用来找到正确的工厂方法.如果工厂不存在,它会抛出一个IllegalArgumentException
.我们可以将其用作创建InvalidCommand
对象的信号.
一个额外的好处是,如果你也可以使方法create(String cmd)
公开,如果你也可以这样构建一个Command对象编译时间检查可用于其余的代码.然后,您可以使用CommandFactory.START.create(String cmd
)来创建Command对象.
最后一个好处是您可以在Javadoc文档中轻松创建所有可用命令的列表.
我认为你的命令字符串映射是好的.您甚至可以将字符串命令名称分解为构造函数(即,不应该让StartCommand知道它的命令是"START"?)如果您可以这样做,那么实例化命令对象要简单得多:
Class c = commandMap.get(cmdName); if (c != null) return c.newInstance(); else throw new IllegalArgumentException(cmdName + " is not as valid command");
另一种选择是enum
使用指向类的链接创建所有命令(假设所有命令对象都实现CommandInterface
):
public enum Command { START(StartCommand.class), END(EndCommand.class); private Class extends CommandInterface> mappedClass; private Command(Class extends CommandInterface> c) { mappedClass = c; } public CommandInterface getInstance() { return mappedClass.newInstance(); } }
因为枚举的toString是它的名字,你可以EnumSet
用来定位正确的对象并从内部获取类.