2

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

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, 的值barenum. 它就像一个静态属性,但enum没有延迟初始化。我可以制作Foo.getBar()抽象类并使用匿名类,但是我每次都必须支付设置费用。

有没有一种便宜的方法来为enums 的属性添加惰性初始化?

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

  1. getBar()被称为数百万次。它一定是快速致盲。

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

    对于附加点,单元测试应该能够覆盖此行为。

  3. 实例必须延迟创建。

我们尝试在 Spring 中将值注册为 bean 的一种解决方案:

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

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

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

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

4

5 回答 5

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

您可以尝试以某种方式优化同步部分,但这种方式可能会因您的具体用例而异。

于 2010-11-19T11:50:47.763 回答
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的,因此可以创建任意多个 any 的实例。因此,此解决方案不符合 Singleton 要求(OP 中的#2)。更糟糕的是,getBar()可以轻松返回对一个尚未初始化的实例的引用IBar

于 2010-11-19T11:56:16.587 回答
1

以下是线程安全惰性初始化的,结合了两种模式:Enum SingletonInitialization-On-Demand Holder。这是避免不必要同步的唯一惰性方法,在抽象工厂模式的情况下,每次调用都会发生这种情况,而在这种情况下,它只会在静态嵌套类初始化的情况下发生一次在定义是惰性的:getBar()

public enum Foo {
    VAL1() {
        @Override
        public IBar getBar() {
            return LazyVAL1.BAR;
        }
    },
    VAL2() {
        @Override
        public IBar getBar() {
            return LazyVAL2.BAR;
        }
    };

    private static class LazyVAL1 {
        public static final IBar BAR = new Bar();
    }

    private static class LazyVAL2 {
        public static final IBar BAR = new FooBar();
    }

    public abstract IBar getBar();
}
于 2011-12-29T09:33:08.230 回答
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 (...) {
...
}
于 2010-11-19T11:46:01.317 回答
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;
    }
}
于 2010-11-19T12:03:40.680 回答