Java中的可配置枚举

时间:2010-11-19 11:40:57

标签: java enums lazy-initialization

我正在寻找一种更好的模式来实现这样的东西:

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?

的属性添加延迟初始化

[编辑] 为了明确这一点:

  1. getBar()被召唤数百万次。它必须快速致盲。

  2. 我们在这里谈论单身人士(就像enum本身一样)。只能创建一个实例。

    对于其他要点,单元测试应该能够覆盖此行为。

  3. 必须懒惰地创建实例。

  4. 我们试图在Spring中将值注册为bean的一个解决方案:

    <bean id="VAL1.bar" class="...." />
    

    这允许我们在运行时指定值并在测试中覆盖它们。不幸的是,这意味着我们必须以某种方式将ApplicationContext注入enum。所以我们需要一个全局变量。 畏缩

    更糟糕的是:在getBar()中查找值太慢了。我们可以synchronize getBar()并使用if(bar!= null)bar=context.get(name()+".bar");来解决此问题。

    但有没有一种方法可以像使用enum值本身一样安全和快速?

5 个答案:

答案 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 SingletonInitialization-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;
    }
}