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

时间:2016-03-28 22:53:32

标签: java reflection instantiation abstract-factory

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

我知道有以下选项:

  1. 抽象工厂模式的使用
  2. 使用反射来创建给定类名的实例
  3. 使用反射从给定的类对象创建实例
  4. 这些选项的优缺点是什么?

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

    以下是相关问题列表:

1 个答案:

答案 0 :(得分:2)

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

答案很长:

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

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

    使用Class::forName

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

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

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

    GUI与类名的耦合

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

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

    流量控制的例外

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

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

    反思的安全问题

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

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

    使用特殊类加载器的反射

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

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

    带参数的构造函数

    给定的例子仍然很简单,因为它不使用构造函数参数。使用具有构造函数参数的类似模式是很常见的。在这种情况下,解决方案2,3和4变得丑陋,请参阅Can I use Class.newInstance() with constructor arguments?

    解决方案1只需将Supplier更改为与构造函数签名匹配的功能接口。

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

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

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

    代码复杂性

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

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

    上面已经讨论了异常处理。

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

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

    代码

    通用代码

    public interface Fruit {
        public static void printOptional(Optional<Fruit> 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<String, Supplier<Fruit>> 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<String, Supplier<Fruit>> createMap() {
            Map<String, Supplier<Fruit>> result = new HashMap<>();
            result.put("apple", Apple::new);
            result.put("banana", Banana::new);
            return result;
        }
    
        private static Optional<Fruit> create(
                Map<String, Supplier<Fruit>> 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<Fruit> create(String userChoice) {
            try {
                return Optional.of((Fruit) Class.forName(userChoice).newInstance());
            } catch (InstantiationException
                   | IllegalAccessException
                   | ClassNotFoundException e) {
                return Optional.empty();
            }
        }
    }
    

    使用地图反射(3)

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