代码之家  ›  专栏  ›  技术社区  ›  Aaron Digulla

Java中的可配置枚举

  •  2
  • Aaron Digulla  · 技术社区  · 15 年前

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

    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 它与 枚举 .它就像一个静态属性,但是 枚举 没有延迟初始化。我可以 Foo.getBar() 抽象并使用匿名类,但这样一来,我每次都必须支付安装费用。

    有没有一种便宜的方法可以为属性添加lazy init 枚举 s

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

    1. getBar() 被称为数百万次。它一定很快就失明了。

    2. 我们这里说的是单身(就像 枚举 本身)。只能创建一个实例。

      另外,单元测试应该能够覆盖这种行为。

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

    我们尝试过的一种解决方案是在春季将值注册为bean:

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

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

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

    但是,有没有一种没有这种功能的方法能像使用 枚举 重视自己?

    5 回复  |  直到 6 年前
        1
  •  2
  •   Roman    15 年前

    只需用抽象工厂模式替换枚举。

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

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

        2
  •  2
  •   charlie    14 年前

    您可以使用执行延迟初始化的“值持有者”添加一级间接寻址:

    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 可以创建。因此,该解决方案不满足单例需求(OP中的#2)。更糟糕的是, getBar() 可以轻松返回对尚未初始化的 伊巴尔 .

        3
  •  1
  •   Vladimir Ivanov    15 年前

    尝试在枚举中存储一个类而不是一个对象,当需要时,只需通过类实例化它。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 (...) {
    ...
    }
    
        4
  •  1
  •   charlie    14 年前

    以下是两者 线程安全 和 懒惰初始化 ,结合两种模式: Enum Singleton 和 Initialization-On-Demand Holder .这是唯一的 懒惰的 避免不必要的 同步化 ,而在 Abstract Factory 模式发生在 每一个 仅有一个的 getBar() 调用,而在这种情况下它会发生 只有一次 万一 Static Nested Class 初始化,也就是 懒惰的 通过 definition :

    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();
    }
    
        5
  •  0
  •   Andreas Dolk    15 年前

    根据弗拉基米尔的回答——这可以做到。它只会创建类对象,实例应根据需要延迟创建:

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