这段代码有一些非常不令人满意的地方:
/*
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.
..。然后使用反射来创建从Map中查找的适当类的实例。然而,虽然在概念上很优雅,但这涉及到大量的反射代码,无论谁继承了这段代码,都可能不会欣赏这些代码-尽管这些成本可能会被好处所抵消。将值硬编码到commandMap中的所有行几乎都和if块一样难闻。
如果工厂的构造函数能够扫描类路径中Command的子类,查询它们的字符串表示形式,并自动将它们添加到它的指令表中,那就更好了。
那么--我该如何进行重构呢?
我猜有些框架是免费提供给我这类东西的。让我们假设我无法将这些东西迁移到这样的框架中。
发布于 2008-09-22 14:58:14
我认为你的字符串到命令的映射很好。您甚至可以将字符串命令名分解为构造函数(即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
来定位正确的对象并从内部获取类。
发布于 2008-09-22 15:42:51
下面的代码如何:
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
)来创建命令对象。
最后一个好处是,您可以轻松地在Javadoc文档中创建所有可用命令的列表。
发布于 2008-09-22 15:00:57
除了
cmd.subString(0,8).trim();
部分,这在我看来并不是很糟糕。您可以使用Map并使用反射,但是,根据您添加/更改命令的频率,这可能不会给您带来太多好处。
您可能应该记录为什么只需要前8个字符,或者可能更改协议,以便更容易找出该字符串的哪一部分是命令(例如,在命令关键字后面放一个像':‘或';’这样的标记)。
https://stackoverflow.com/questions/115269
复制相似问题