当前位置: 首页 > 面试题库 >

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

终逸仙
2023-03-14
问题内容

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

我知道有以下几种选择:

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

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

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


问题答案:

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();
        }
    }
}


 类似资料:
  • 我用<code>列表视图</code>创建了一个活动。这是一个朋友。 我想让它选择将其添加到另一个。 我不知道选择哪个<code>视图</code>最好<代码>循环视图或

  • 问题内容: 总览 我正在客户端(Android手机)和服务器(Windows Server)之间来回发送消息。使用基于TCP的持久连接,哪种协议将是最佳解决方案。我正在研究性能,可伸缩性,消息大小和电池寿命。消息必须按顺序到达目的地,并且不能重复。 MQTT 这似乎是更好的解决方案,但似乎很少有大量用户使用大型实现的示例。我不确定是否可以将其集成到Windows服务器中,或者是否必须运行其他应用程

  • 我要跟着SQL桌子 我需要从这个表中选择所有记录,其中值是25的和。因此,对于键A,应该显示ID为1和2的记录。对于键B,记录ID为3,对于键C,记录ID应为5。 我正在尝试类似的东西,但这是给一个错误,仍在尝试。 有什么建议可以得到想要的套装吗? 编辑:添加服务器名称。

  • 本文向大家介绍jQuery选择器实例应用,包括了jQuery选择器实例应用的使用技巧和注意事项,需要的朋友参考一下 刚学完jQuery选择器,闲来无事,照着书上的范例敲了一段代码(HTML和CSS抄自书上),自己试着写了写jQuery的代码,感觉相当轻便啊。 显示效果: 功能说明:   1、点击上边的图书分类一栏,实现向下的伸缩扩展,可以控制分类的显示状态;   2、“简化”功能点击后实现分类显示

  • 我想让程序根据我的扫描仪输入从ArrayList中进行选择。比如,我写早餐和甜食,它必须随机列出早餐甜食,并打印随机索引。 我还在学习Java,我只是在玩,并试图编码小项目来训练它。 下面是我已经写过的课程: 我是否可以将列表存储在变量中,可能是这样的: 我知道很难理解我,但英语不是我的主要语言,希望它能被理解。

  • 主要内容:所需步骤,示例代码在本教程将演示如何在JDBC应用程序中选择一个数据库。 在执行以下示例之前,请确保您已经准备好以下操作: 具有数据库管理员权限,以在给定模式中创建数据库。 要执行以下示例,需要用实际用户名和密码替换这里用户名()和密码()。 MySQL或数据库已启动并运行。 所需步骤 使用JDBC应用程序选择数据库需要以下步骤: 导入包:需要包含包含数据库编程所需的JDBC类的包。 大多数情况下,使用就足够了。