我正在寻找一种更好的模式来实现这样的东西:
public static enum Foo {
VAL1( new Bar() ),
VAL2( new FooBar() );
private final bar;
private Foo( IBar bar ) {
this.bar = bar;
}
public IBar getBar() { return bar; }
}
问题是访问enum
会导致副作用。假设Bar
打开数据库连接等。因此,即使我只需要VAL2
,我也必须付出代价来设置VAL1
。
OTOH,bar
的值与enum
紧密耦合。它就像一个静态属性,但enum
没有延迟初始化。我可以使Foo.getBar()
抽象并使用匿名类,但是,我每次都必须支付设置价格。
是否有一种廉价的方法为enum
s?
[编辑] 为了明确这一点:
getBar()
被召唤数百万次。它必须快速致盲。
我们在这里谈论单身人士(就像enum
本身一样)。只能创建一个实例。
对于其他要点,单元测试应该能够覆盖此行为。
必须懒惰地创建实例。
我们试图在Spring中将值注册为bean的一个解决方案:
<bean id="VAL1.bar" class="...." />
这允许我们在运行时指定值并在测试中覆盖它们。不幸的是,这意味着我们必须以某种方式将ApplicationContext
注入enum
。所以我们需要一个全局变量。 畏缩
更糟糕的是:在getBar()
中查找值太慢了。我们可以synchronize
getBar()
并使用if(bar!= null)bar=context.get(name()+".bar");
来解决此问题。
但有没有一种方法可以像使用enum
值本身一样安全和快速?
答案 0 :(得分:2)
只需用抽象工厂模式替换枚举。
UPD :您可以尝试这样的事情:
public interface Factory {
IBar getBar();
}
public class BarFactory implements Factory {
private IBar barInstance;
public synchronized IBar getBar() {
if (barInstance == null) {
barInstance = new Bar();
}
return barInstance;
}
}
public class FooBarFactory implements Factory {
private IBar barInstance;
public synchronized IBar getBar() {
if (barInstance == null) {
barInstance = new FooBar();
}
return barInstance;
}
}
您可以尝试以某种方式优化同步部分,但这种方式可能因具体用例而异。
答案 1 :(得分:2)
您可以使用执行延迟初始化的“值持有者”添加一个间接级别:
abstract class BarHolder {
IBar bar;
abstract IBar createBar();
IBar getBar() {
if (bar == null) {
bar = createBar();
}
return bar;
}
}
现在,像这样调整你的枚举:
public static enum Foo {
VAL1(new BarHolder() {
IBar createBar() { return new Bar(); }
)},
VAL2(new BarHolder() {
IBar createBar() { return new FooBar(); }
)};
private final BarHolder barHolder;
private Foo(BarHolder barHolder) {
this.barHolder = barHolder;
}
public IBar getBar() { return barHolder.getBar(); }
}
警告:由于非线程安全,因此可以创建任意IBar
的任意多个实例。因此,该解决方案不符合Singleton要求(OP中的#2)。更糟糕的是,getBar()
可以轻松返回对IBar
尚未初始化的实例的引用。
答案 2 :(得分:1)
尝试在枚举中存储一个对象而不是一个类,并在需要时,通过Class.newInstance()实例化它。
public static enum Foo {
VAL1(Bar.class),
VAL2(FooBar.class);
private final Class<...> barClass;
private Foo( Class<? extends IBar> barClass ) {
this.barClass = barClass;
}
public Class< ? extends IBar> getBarClass() { return barClass; }
}
/// client code
try {
IBar instance = Class.newInstance(Foo.VAL1.getBarClass());
} catch (...) {
...
}
答案 3 :(得分:1)
以下是线程安全和延迟初始化,它们结合了两种模式:Enum Singleton和Initialization-On-Demand Holder。这是避免不必要的同步的唯一懒惰方式,对于每个单{{1},会出现Abstract Factory模式调用,而在这种情况下,Static Nested Class初始化时出现一次,definition为懒惰:
getBar()
答案 4 :(得分:0)
根据弗拉基米尔的回答 - 这可以做到。它只会创建类对象,应该根据需要懒惰地创建实例:
public static enum Foo {
VAL1(Bar.class),
VAL2(FooBar.class);
private Class<? extends IBar> clazz;
private IBar bar = null;
private Foo( Class<? extends IBar> clazz) {
this.clazz = clazz;
}
public IBar getBar() {
if (bar == null)
bar = clazz.newInstance();
}
return bar;
}
}