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問題は、原因の副作用にアクセスすることです。DBBar接続などを開きます。そのため、必要なだけでも、VAL2セットアップの代償を払わなければなりませんVAL1

OTOH、 の値barは に密接に結合されていenumます。これは静的属性に似ていますがenum、遅延初期化はありません。抽象化して匿名クラスを使用することもできFoo.getBar()ますが、その場合、毎回セットアップ料金を支払う必要があります。

s の属性に遅延初期化を追加する安価な方法はありenumますか?

[編集]これを明確にするために:

  1. getBar()何百万回も呼ばれています。目がくらむほど速いに違いない。

  2. ここではシングルトンについて話しています (enumそれ自体と同じように)。単一のインスタンスのみを作成する必要があります。

    追加のポイントとして、単体テストはこの動作をオーバーライドできる必要があります。

  3. インスタンスは遅延して作成する必要があります。

Spring で値を Bean として登録するために試した 1 つの解決策:

<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

遅延初期化を行う「値ホルダー」を使用して、1 レベルの間接化を追加できます。

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

警告:これはスレッドセーフではないため、 any の任意の数のインスタンスをIBar作成できます。したがって、このソリューションはシングルトン要件 (OP の #2) を満たしていません。さらに悪いことに、getBar()まだ初期化されていない のインスタンスへの参照を簡単に返すことができますIBar

于 2010-11-19T11:56:16.587 に答える
1

以下は、スレッドセーフレイジー初期化されたもので、列挙型シングルトン初期化オンデマンドホルダーの2つのパターンを組み合わせたものです。これは、不要な同期を回避する唯一の怠惰な方法です。これは、抽象ファクトリパターンの場合、すべての呼び出しに対して発生しますが、この場合、静的ネストクラスの初期化の場合に1回だけ発生します。これは、定義怠惰です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

オブジェクトではなくクラスを enum に格納して、必要に応じて 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 に答える