【问题标题】:Requesting item from Factory without hard-coding every case向工厂索取物品,无需对每个案例进行硬编码
【发布时间】:2015-11-25 13:46:09
【问题描述】:

我有一个名为 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 {...}

而工厂应该接受一个字符串并返回相应命令的一个实例:

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 吗?

【问题讨论】:

  • 你能用类而不是字符串来调用“getCommand()”吗?
  • 不,字符串是从 JTextPane 中读取并手动输入的。
  • 为什么不想使用反射?虽然我同意在您不需要它时远离​​它通常是明智的(并避免“聪明”的技巧),但这个用例非常适合它。

标签: java class interface factory hardcode


【解决方案1】:

生成完全限定名(package.class)并调用

Class<?> myClass = Class.forName(className);

然后调用

myClass.newInstance();

【讨论】:

  • 他不假思索地问。
  • 我很好奇除了反射是否还有其他方法,但如果没有,我可以使用它。谢谢!
  • Class.forName 不是 java.lang.reflect 的一部分。
  • 任何其他方式都会得到相同的结果,只是现在您将拥有一个比 Class.forName 调试更少的并行“反射”模块
【解决方案2】:

如果您的命令是无状态的、线程安全的对象,您可以创建急切创建的实例的映射并重复使用这些实例:

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
        }
    }
}

为了这个有任何好处(并满足“不更改类”的要求)但是工厂将不是是一个静态类,具有实例(而不是静态)getCommand() 方法.然后可以使用 DI 框架注入可用的命令。


如果您需要为每个调用创建一个 Command 实现的新实例,那么如果不使用可怕的 if...else if 链,就无法真正避免反射。不过,我不会太担心在这种情况下使用它,Class.newInstance() 可读性很强,效率也不是很低。

尽管如此,我仍然会坚持使用这种地图方法,以使其灵活且可通过配置进行扩展。代码将与 Map&lt;String, Class&gt;return availableCommands.get(s).newInstance() 和更多异常检查类似。


第三种可能的方法是为每种命令类型创建一个单独的工厂类,并使用 Map&lt;String, SpecificCommandFactory&gt;,您可以在其中查询适当的工厂,然后使用该工厂获取该特定命令类的新实例 -但这是很多样板代码,可能非常难以阅读,因此仅在您确实需要将工厂可用的命令分开时才适用:

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
        }
    }
}

【讨论】:

  • 这将给你每个类类型一个对象,而不是每次一个新对象。
  • 您也可以让Map&lt;String, Class&gt; 每次都构造一个新对象
  • 是的,但如果你这样做,那你比Class.forName好多少?
  • @Shloim 是的,这是真的,只有在没有问题的情况下才能使用它——这就是为什么我写了“如果你的命令是无状态的、线程安全的对象......”
【解决方案3】:

您可以为此使用某种委托工厂。您可以为不同的命令保存工厂,而不是在地图中保存对对象的引用(如 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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-10
    • 1970-01-01
    • 2021-11-05
    相关资源
    最近更新 更多