我有Fruit
两个实现Apple
和的接口Banana
。我想创建一个Fruit
实例。具体实现是应该由用户选择Apple
还是Banana
应由用户进行选择。我尚未设计用户界面,因此没有限制用户如何进行此选择。
我知道有以下几种选择:
这些选项的优缺点是什么?
请注意,尽管有几种类似的问题在讨论一种方法或另一种方法,但我没有找到一个比较。
tl; dr 我建议使用抽象工厂模式。
长答案:
为了比较这些方法,我在下面附加了四个可能的解决方案。总结如下:
Class::forName
首先,反射解决方案2和3用提供类名称的String标识类对象。这样做很不好,因为它破坏了自动重构工具:重命名类时,不会更改String。而且,不会有编译器错误。该错误将仅在运行时可见。
请注意,这不依赖于重构工具的质量:在解决方案2中,提供类名称的String可能会以您认为最模糊的方式构造。它甚至可以由用户输入或从文件中读取。重构工具无法完全解决此问题。
解决方案1和4没有这些问题,因为它们直接链接到类。
由于解决方案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不存在此问题,因为它允许您将任何可创建水果的函数放入地图中。
以下是介绍代码复杂性的元素以及出现的解决方案:
上面已经讨论了异常处理。
映射是代码的一部分,它将用户提供的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";
}
}
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);
}
}
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();
}
}
}
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();
}
}
}
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类的包。 大多数情况下,使用就足够了。