我应该如何根据用户选择选择应该实例化哪个具体实现?

我有一个接口Fruit有两个实现AppleBanana 。 我想创建一个Fruit实例。 应该由用户选择具体实现应该是Apple还是Banana 。 我还没有设计用户界面,所以用户做出这个选择没有限制。

我知道有以下选择:

  1. 使用抽象工厂模式
  2. 使用reflection从给定的类名创建实例
  3. 使用reflection从给定的类对象创建实例

这些选项的优缺点是什么?


请注意,虽然有几个类似的问题讨论了一种或另一种方法,但我没有找到一个比较。

以下是相关问题列表:

  • 为什么Class.newInstance()“邪恶”?
  • 我可以将Class.newInstance()与构造函数参数一起使用吗?
  • 如何制作类的ArrayList?
  • Class.newInstance()是否遵循“抽象工厂”设计模式?

tl; dr我建议使用抽象工厂模式。

答案很长:

为了比较这些方法,我在下面附上了四种可能的解决方 以下是摘要:

  1. 使用抽象工厂模式
  2. 使用由用户直接选择的String来按名称实例化一个类
  3. 获取由用户直接选择的String,并将其转换为另一个String,以按名称实例化一个类
  4. 获取由用户直接选择的String,并将其转换为Class对象以实例化该类

对照

使用Class::forName

首先,reflection解决方案2和3使用提供类名的String来标识类对象。 这样做很糟糕,因为它会破坏自动重构工具:重命名类时,不会更改String。 此外,不会有编译器错误。 该错误仅在运行时可见。

请注意,这不取决于重构工具的质量:在解决方案2中,提供类名的String可能以您能想到的最隐蔽的方式构造。 它甚至可以由用户输入或从文件中读取。 重构工具无法完全解决此问题。

解决方案1和4没有这些问题,因为它们直接链接到类。

GUI与类名的耦合

由于解决方案2直接使用用户提供的String进行reflection以按名称标识类,因此​​GUI将耦合到您在代码中使用的类名。 这很糟糕,因为这要求您在重命名类时更改GUI。 重命名类应该始终尽可能简单,以便轻松重构。

解决方案1,3和4没有这个问题,因为它们将GUI使用的String转换为其他内容。

流量控制的例外情况

当使用forNamenewInstance的reflection方法时,解决方案2,3和4必须处理exception。 解决方案2甚至必须使用流控制的exception,因为它没有任何其他方法来检查输入是否有效。 使用流量控制的exception通常被认为是不好的做法。

解决方案1没有此问题,因为它不使用reflection。

反思的安全问题

解决方案2直接使用用户提供的String进行reflection。 这可能是一个安全问题。

解决方案1,3和4没有这个问题,因为它们将用户提供的字符串转换为其他字符串。

与特殊类加载器的reflection

您无法在所有环境中轻松使用此类reflection。 例如,使用OSGi时可能会遇到问题。

解决方案1没有此问题,因为它不使用reflection。

带参数的构造函数

给定的示例仍然很简单,因为它不使用构造函数参数。 使用具有构造函数参数的类似模式是很常见的。 在这种情况下,解决方案2,3和4变得丑陋,请参阅我可以将Class.newInstance()与构造函数参数一起使用吗?

解决方案1只需将Supplier更改为与构造函数签名匹配的function界面。

使用工厂(方法)创建复杂的水果

解决方案2,3和4要求您通过构造函数实例化水果。 但是,这可能是不合需要的,因为您通常不希望将复杂的初始化逻辑放入构造函数中,而是放入工厂(方法)。

解决方案1没有此问题,因为它允许您将任何创建水果的function放入地图中。

代码复杂性

以下是引入代码复杂性的元素,以及它们出现的解决方案:

  • 在1,3和4中创建地图
  • 2,3和4中的exception处理

上面已经讨论了exception处理。

映射是代码的一部分,用于将用户提供的String转换为其他内容。 因此,地图解决了上述许多问题,这意味着它有助于实现目的。

请注意,映射也可以由List或数组替换。 然而,这并没有改变上述任何结论。

共同代码

 public interface Fruit { public static void printOptional(Optional optionalFruit) { if (optionalFruit.isPresent()) { String color = optionalFruit.get().getColor(); System.out.println("The fruit is " + color + "."); } else { System.out.println("unknown fruit"); } } String getColor(); } public class Apple implements Fruit { @Override public String getColor() { return "red"; } } public class Banana implements Fruit { @Override public String getColor() { return "yellow"; } } 

抽象工厂(1)

 public class AbstractFactory { public static void main(String[] args) { // this needs to be executed only once Map> map = createMap(); // prints "The fruit is red." Fruit.printOptional(create(map, "apple")); // prints "The fruit is yellow." Fruit.printOptional(create(map, "banana")); } private static Map> createMap() { Map> result = new HashMap<>(); result.put("apple", Apple::new); result.put("banana", Banana::new); return result; } private static Optional create( Map> map, String userChoice) { return Optional.ofNullable(map.get(userChoice)) .map(Supplier::get); } } 

反思(2)

 public class Reflection { public static void main(String[] args) { // prints "The fruit is red." Fruit.printOptional(create("stackoverflow.fruit.Apple")); // prints "The fruit is yellow." Fruit.printOptional(create("stackoverflow.fruit.Banana")); } private static Optional create(String userChoice) { try { return Optional.of((Fruit) Class.forName(userChoice).newInstance()); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { return Optional.empty(); } } } 

地图reflection(3)

 public class ReflectionWithMap { public static void main(String[] args) { // this needs to be executed only once Map map = createMap(); // prints "The fruit is red." Fruit.printOptional(create(map, "apple")); // prints "The fruit is yellow." Fruit.printOptional(create(map, "banana")); } private static Map createMap() { Map result = new HashMap<>(); result.put("apple", "stackoverflow.fruit.Apple"); result.put("banana", "stackoverflow.fruit.Banana"); return result; } private static Optional create( Map map, String userChoice) { return Optional.ofNullable(map.get(userChoice)) .flatMap(ReflectionWithMap::instantiate); } private static Optional instantiate(String userChoice) { try { return Optional.of((Fruit) Class.forName(userChoice).newInstance()); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { return Optional.empty(); } } } 

类地图反思(4)

 public class ReflectionWithClassMap { public static void main(String[] args) { // this needs to be executed only once Map> map = createMap(); // prints "The fruit is red." Fruit.printOptional(create(map, "apple")); // prints "The fruit is yellow." Fruit.printOptional(create(map, "banana")); } private static Map> createMap() { Map> result = new HashMap<>(); result.put("apple", Apple.class); result.put("banana", Banana.class); return result; } private static Optional create( Map> map, String userChoice) { return Optional.ofNullable(map.get(userChoice)) .flatMap(ReflectionWithClassMap::instantiate); } private static Optional instantiate(Class c) { try { return Optional.of(c.newInstance()); } catch (InstantiationException | IllegalAccessException e) { return Optional.empty(); } } }