从工厂请求项目而不对每个案例进行硬编码
Requesting item from Factory without hard-coding every case
我有一个名为 Command 的界面:
public interface Command {
// irrelevant code
}
还有大约 15 个 类 实现了这个接口:
public class Comm_A implements Command {
// irrelevant code
}
public class Comm_B implements Command {...}
....
public class Comm_XZ implements Command {...}
工厂应该接受一个字符串和return一个相应命令的实例:
public class CommandFactory {
public static Command getCommand(String s) {
if (s.equals("Comm_A")) return new Comm_A();
else ... // each particular case
}
}
考虑到我希望能够在不更改工厂代码的情况下添加新命令。
我可以让它自动尝试创建相应的实例而无需对每个 "Comm_A"..."Comm_XZ" 案例进行硬编码并且不使用 java.lang.reflect 吗?
生成完全限定名称 (package.class) 并调用
Class<?> myClass = Class.forName(className);
然后调用
myClass.newInstance();
如果您的命令是无状态的、线程安全的对象,您可以创建一个急切创建的实例映射并重复使用这些实例:
public class CommandFactory {
private final Map<String, Command> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s);
} else {
// handle error state
}
}
}
为了这有任何好处(并满足 "no changes to the class" 要求)但是工厂 不是 是静态的 class,实例(而不是比静态)getCommand()
方法。然后可以使用 DI 框架注入可用命令。
如果您需要为每个调用创建一个新的 Command
实现实例,那么如果不使用可怕的 if...else if
链,您就无法真正避免反射。不过,我不会太担心在这种情况下使用它,Class.newInstance()
可读性很强,而且效率不低。
即使如此,我仍然会坚持使用地图方法,以使其灵活且可通过配置进行扩展。代码与 Map<String, Class>
、return availableCommands.get(s).newInstance()
和更多异常检查类似。
第三种可能的方法是为每个命令类型创建一个单独的工厂 class,并有一个 Map<String, SpecificCommandFactory>
,您可以在其中查询适当的工厂,然后使用该工厂获得该特定命令的新实例 class - 但这是很多样板代码,可能很难读,因此仅当您确实需要分离工厂提供的命令时才适用:
public iterface SpecificCommandFactory {
Command createCommand();
}
public class Comm_AFactory implements SpecificCommandFactory {
public Comm_A createCommand() {
return new Comm_A();
}
}
public class CommandFactory {
private final Map<String, SpecificCommandFactory> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s).createCommand();
} else {
// handle error state
}
}
}
您可以为此使用某种委托工厂。您可以为不同的命令保留工厂,而不是在地图中保留对对象的引用(如 Jiri Tousek 的回答):
public class CommandFactory{
private static final Map<String, Callable<Command>> commands = new HashMap<>();
public static void registerCommand(String command, Callable<Command> factory){
commands.put(command, factory);
}
public static Command getCommand(String command){
Callable<Command> factory = commands.get(command);
if(factory != null){
return factory.call();
}
return null;
}
}
我有一个名为 Command 的界面:
public interface Command {
// irrelevant code
}
还有大约 15 个 类 实现了这个接口:
public class Comm_A implements Command {
// irrelevant code
}
public class Comm_B implements Command {...}
....
public class Comm_XZ implements Command {...}
工厂应该接受一个字符串和return一个相应命令的实例:
public class CommandFactory {
public static Command getCommand(String s) {
if (s.equals("Comm_A")) return new Comm_A();
else ... // each particular case
}
}
考虑到我希望能够在不更改工厂代码的情况下添加新命令。
我可以让它自动尝试创建相应的实例而无需对每个 "Comm_A"..."Comm_XZ" 案例进行硬编码并且不使用 java.lang.reflect 吗?
生成完全限定名称 (package.class) 并调用
Class<?> myClass = Class.forName(className);
然后调用
myClass.newInstance();
如果您的命令是无状态的、线程安全的对象,您可以创建一个急切创建的实例映射并重复使用这些实例:
public class CommandFactory {
private final Map<String, Command> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s);
} else {
// handle error state
}
}
}
为了这有任何好处(并满足 "no changes to the class" 要求)但是工厂 不是 是静态的 class,实例(而不是比静态)getCommand()
方法。然后可以使用 DI 框架注入可用命令。
如果您需要为每个调用创建一个新的 Command
实现实例,那么如果不使用可怕的 if...else if
链,您就无法真正避免反射。不过,我不会太担心在这种情况下使用它,Class.newInstance()
可读性很强,而且效率不低。
即使如此,我仍然会坚持使用地图方法,以使其灵活且可通过配置进行扩展。代码与 Map<String, Class>
、return availableCommands.get(s).newInstance()
和更多异常检查类似。
第三种可能的方法是为每个命令类型创建一个单独的工厂 class,并有一个 Map<String, SpecificCommandFactory>
,您可以在其中查询适当的工厂,然后使用该工厂获得该特定命令的新实例 class - 但这是很多样板代码,可能很难读,因此仅当您确实需要分离工厂提供的命令时才适用:
public iterface SpecificCommandFactory {
Command createCommand();
}
public class Comm_AFactory implements SpecificCommandFactory {
public Comm_A createCommand() {
return new Comm_A();
}
}
public class CommandFactory {
private final Map<String, SpecificCommandFactory> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s).createCommand();
} else {
// handle error state
}
}
}
您可以为此使用某种委托工厂。您可以为不同的命令保留工厂,而不是在地图中保留对对象的引用(如 Jiri Tousek 的回答):
public class CommandFactory{
private static final Map<String, Callable<Command>> commands = new HashMap<>();
public static void registerCommand(String command, Callable<Command> factory){
commands.put(command, factory);
}
public static Command getCommand(String command){
Callable<Command> factory = commands.get(command);
if(factory != null){
return factory.call();
}
return null;
}
}