我应如何根据用户选择来选择应实例化的具体实现?

斯蒂芬·多拉斯(Stefan Dollase)

我有Fruit两个实现Apple的接口Banana我想创建一个Fruit实例。具体实现是应该由用户选择Apple还是Banana应由用户进行选择。我尚未设计用户界面,因此对用户如何选择此选项没有限制。

我知道有以下几种选择:

  1. 抽象工厂模式的用法
  2. 使用反射根据给定的类名创建实例
  3. 使用反射从给定的类对象创建实例的用法

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


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

以下是相关问题的列表:

斯蒂芬·多拉斯(Stefan Dollase)

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

长答案:

为了比较这些方法,我在下面附加了四个可能的解决方案。总结如下:

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

比较

使用 Class::forName

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

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

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

GUI与类名称的耦合

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

解决方案1、3和4不存在此问题,因为它们将GUI使用的String转换为其他字符串。

流量控制的例外

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

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

反思的安全性问题

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

解决方案1、3和4不存在此问题,因为它们将用户提供的String转换为其他字符串。

特殊装载机的反射

您不能在所有环境中轻松使用这种类型的反射。例如,使用OSGi时您可能会遇到问题。

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

带参数的构造函数

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

解决方案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();
        }
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何根据用户选择更新我的税金

来自分类Dev

如何根据我的用户选择重新排序 selectpicker 的选择?

来自分类Dev

允许用户选择宏应刷新的打开窗口

来自分类Dev

我应如何在栏的选择陈述式中加入文字

来自分类Dev

我的用户控件应如何通知我的观点?

来自分类Dev

REACT.JS-未显示选择/选项,应如何显示

来自分类Dev

如何根据用户的选择选择词典?

来自分类Dev

如何根据用户选择在JS中再次重复我的程序?

来自分类Dev

2个类似的Java枚举和方法谁从该枚举应的值来选择决定

来自分类Dev

如何使选择更具体?

来自分类Dev

如何根据用户使用实体框架访问的内容来选择数据

来自分类Dev

如何根据用户在Tableau中选择的日期来控制条形图中的条形数量

来自分类Dev

flask-sqlalchemy:如何制作一个可选择的按钮来根据他的角色 id 选择用户?

来自分类Dev

在Intel上使用ByteBuffers时,我应何时选择Little Endian与Big Endian?

来自分类Dev

从下拉列表中选择的属性,我的模型中是否应同时包含文本和值

来自分类Dev

每当用户更改(即编辑)html表中的列中的值时,应自动重新计算总计吗?我该如何实现?

来自分类Dev

CSV如何根据用户选择删除行

来自分类Dev

如何根据用户选择组合数组?

来自分类Dev

根据用户选择输入来编写html文本

来自分类Dev

根据用户从数组中选择来计算交换价格

来自分类Dev

Django-根据用户在表中的选择删除实例

来自分类Dev

如何根据表单字段选择要实例化的对象类型?

来自分类Dev

我如何根据vbs随机选择的名称来更改此链接?

来自分类Dev

如何将某些组合框项目涂成灰色,应禁止选择

来自分类Dev

在ap:tree上应使用什么事件来选择树节点并具有上下文菜单?

来自分类Dev

SQL-如何根据用户是否存在于另一个表中来选择该用户?

来自分类Dev

用户类应实现IPrincipal和IIdentity吗

来自分类Dev

如何实现选择,以便当用户单击提交按钮时我可以在代码中访问选择的值

来自分类Dev

如何实现选择,以便当用户单击提交按钮时我可以在代码中访问选择的值

Related 相关文章

  1. 1

    如何根据用户选择更新我的税金

  2. 2

    如何根据我的用户选择重新排序 selectpicker 的选择?

  3. 3

    允许用户选择宏应刷新的打开窗口

  4. 4

    我应如何在栏的选择陈述式中加入文字

  5. 5

    我的用户控件应如何通知我的观点?

  6. 6

    REACT.JS-未显示选择/选项,应如何显示

  7. 7

    如何根据用户的选择选择词典?

  8. 8

    如何根据用户选择在JS中再次重复我的程序?

  9. 9

    2个类似的Java枚举和方法谁从该枚举应的值来选择决定

  10. 10

    如何使选择更具体?

  11. 11

    如何根据用户使用实体框架访问的内容来选择数据

  12. 12

    如何根据用户在Tableau中选择的日期来控制条形图中的条形数量

  13. 13

    flask-sqlalchemy:如何制作一个可选择的按钮来根据他的角色 id 选择用户?

  14. 14

    在Intel上使用ByteBuffers时,我应何时选择Little Endian与Big Endian?

  15. 15

    从下拉列表中选择的属性,我的模型中是否应同时包含文本和值

  16. 16

    每当用户更改(即编辑)html表中的列中的值时,应自动重新计算总计吗?我该如何实现?

  17. 17

    CSV如何根据用户选择删除行

  18. 18

    如何根据用户选择组合数组?

  19. 19

    根据用户选择输入来编写html文本

  20. 20

    根据用户从数组中选择来计算交换价格

  21. 21

    Django-根据用户在表中的选择删除实例

  22. 22

    如何根据表单字段选择要实例化的对象类型?

  23. 23

    我如何根据vbs随机选择的名称来更改此链接?

  24. 24

    如何将某些组合框项目涂成灰色,应禁止选择

  25. 25

    在ap:tree上应使用什么事件来选择树节点并具有上下文菜单?

  26. 26

    SQL-如何根据用户是否存在于另一个表中来选择该用户?

  27. 27

    用户类应实现IPrincipal和IIdentity吗

  28. 28

    如何实现选择,以便当用户单击提交按钮时我可以在代码中访问选择的值

  29. 29

    如何实现选择,以便当用户单击提交按钮时我可以在代码中访问选择的值

热门标签

归档