java使用一个通用工厂方法创建实例
我试图找到一种易于扩展的方法,在运行时基于名为NAME的静态字符串类属性创建对象
如何改进这段使用简单if构造的代码
public class FlowerFactory {
private final Garden g;
public FlowerFactory(Garden g) {
this.g = g;
}
public Flower createFlower(final String name) {
Flower result = null;
if (Rose.NAME.equals(name)) {
result = new Rose(g);
} else if (Oleander.NAME.equals(name)) {
result = new Oleander(g);
} else if ... { ... } ...
return result;
}
除非删除构造函数参数,否则不能在这些类上使用newInstance()。我应该构建所有受支持的flower类引用的映射(map),并将constructor参数移动到属性setter方法,还是有其他简单的解决方案
背景信息:我的目标是通过FlowerFactory.getInstance().register(this.NAME, this.class)
实现新Flower类的某种“自我注册”,这意味着从目前为止非常好的答案来看,基于内省的解决方案最适合
# 1 楼答案
一种可能是使用枚举。在最简单的级别上,可以用枚举值替换
Rose.NAME
之类的常量,并维护枚举值和类之间的内部映射以实例化:由于flower类没有默认构造函数,
Class.newInstance()
无法使用,因此通过反射实例化类有点麻烦(尽管可能)。另一种方法是使用Prototype创建新的flower实例这已经确保了可能的花名称和实际的花类之间的映射始终保持同步。添加新flower类时,必须创建新的枚举值,其中包括创建新类实例的映射。然而,enum aproach的问题是,您使用的
Garden
实例在启动时是固定的。(除非您将其作为参数传递给getFlower()
,否则会有失去连贯性的风险,即更难确保在特定花园中创建特定的花组)如果您想更灵活,可以考虑使用Spring将名称和具体(bean)类之间的映射映射到配置文件。然后,您的工厂只需在后台加载一个Spring ApplicationContext,并使用其中定义的映射。每当你引入一个新的flower子类时,你只需要在配置文件中添加一个新行。不过,这种方法以其最简单的形式再次要求您在配置时修复
Garden
bean实例如果您想在运行时在不同的花园之间切换,并确保花园和花卉组之间的一致性,那么使用内部名称映射到花卉类的工厂可能是最佳选择。虽然映射本身可以再次存储在配置中,但您可以在运行时用不同的
Garden
实例实例化不同的工厂实例# 2 楼答案
如果所有的
Flower
都有相同的构造函数签名,那么可以使用反射在构造函数上设置参数显然,这正在进入依赖注入的领域,但也许这就是你正在做的:)
如果构造函数中有很多不同的参数,如果这样做是安全的,那么可以选择每个参数的类型来查找要传入的实例,这有点像Guice所做的
# 3 楼答案
还可以通过将字符串名称存储在映射中来避免一系列if/else
如果你完全控制你的类,你可以直接从字符串名中使用反射来执行实例化,例如
除此之外,您还可以尝试依赖注入框架。其中一些提供了从字符串名称检索对象实例的功能
# 4 楼答案
可以将枚举与抽象工厂方法结合使用:
不过,我不能说这是不是你最好的方式,因为我掌握的信息很少
# 5 楼答案
除了使用枚举或映射之外,如果存在名称到类的简单映射,还可以使用反射
# 6 楼答案
即使有构造函数参数,也可以使用反射:
结合静态名称到类的映射,可以这样实现:
可以在静态初始值设定项块中填充花朵的位置: