1

論理ステートメントを使用せずに、遅延初期化を 1 つだけ有効にするように設計された、次の抽象クラスを模倣しようとしています。簡単にするために、スレッド セーフに必要な同期要素は無視しています。

abstract class Thunk<T>
{
    private boolean initiated = false;
    private T value;

    public T get()
    {
        if(!initiated) // not using (value == null)
        {
            value = compute();
            initiated = true;
        }

        return value;
    }

    abstract protected T compute();
}

次の抽象クラスのインスタンスを子がハッキングして、同じ変数を複数回初期化できますか?

abstract class Thunk<T>
{
    private T value;
    private Computer<T> computer;

    public Thunk()
    {
        computer = new Computer<T>(this);
    }

    public T get()
    {
        value = computer.getValue();    
        return value;
    }

    abstract protected T compute();

    private class Computer<T> 
    {
        private static final String TAG = "Computer";

        private Thunk<T> thunk;
        private T value;
        private Computer<T> computer;

        public Computer(Thunk<T> thunk)
        {
            Log.d(TAG, "constructed");
            this.thunk = thunk;
            computer = this;
        }

        public T getValue() 
        {
            Log.d(TAG + ".getValue()", "");
            value = computer.computeValue();
            return value;
        }

        protected T computeValue() 
        {
            Log.d(TAG + ".computeValue()", "");
            value = thunk.compute();
            computer = new DumbComputer<T>(thunk, value);
            return value;
        }

        //this is for maximal encapsulation
        private class DumbComputer<T> extends Computer<T>
        {
            private static final String TAG = "DumbComputer";
            private T value;

            public DumbComputer(Thunk<T> thunk, T value) 
            {
                super(thunk);
                Log.d(TAG + ".contructed()", "booki");
                this.value = value; 
            }

            //overriding so that value will be calculated only once.
            @Override
            protected T computeValue() 
            {
                Log.d(TAG + ".computeValue()", "");
                return value;
            }
        }

    }
}
4

3 に答える 3

1

はい、getメソッドをオーバーライドします。

getこれを修正するには、をメソッドにすることができますfinal。これにより、オーバーライドが防止され、シングルトンのような動作が得られます。

作成したコードはスレッドセーフではないことに注意してください。

メソッドを作成することでスレッドセーフを実現できます(問題が発生し、メソッドがホットスポットであることがわかるまでsynchronized、パフォーマンスについて心配する必要はありません。低速の正しいコードは高速の不正なコードよりも優れており、JVMは最適化に非常に優れているためです。ロック。このクラスの特定のロックが過度に高温であることがわかった場合は、いくつかのトリックを使用して速度を上げることができます...ただし、まだ心配する必要はありません

また、レイジーinitのリソースホルダー内部クラスパターン(このクラスが必要とするため、ユースケースには適用されません。シングルトンにのみ使用されます)は、シングルトンの最高のレイジーinitが必要な場合に使用できます。

更新(コメントはフォーマットをサポートしていないため、コメントに応答します)

これを行う:

abstract class Thunk<T>
{
    private boolean initiated = false;
    private T value;

    public synchronized final T get()
    {
        if(!initiated) // not using (value == null)
        {
            value = compute();
            initiated = true;
        }

        return value;
    }

    abstract protected T compute();
}

これは、おそらく機能する可能性のある最も単純なコードです。そのコードを「改善」しようとすることさえ夢にも思わないでください。改善することはできますが、クラスの使用方法によって改善が異なり、改善の複雑さにより、コードが実行しようとしていることが隠されます。動作することができる最も単純なものから始めて、そこから進んでください。

シンプルに愚かにしてください

そして、あなたがまだ持っていない問題を解決しないでください

于 2012-11-25T08:44:43.637 に答える
1

パターン

public final void f() {
    ...
    X x = ...;
    g(x);
    ...
}

abstract protected void g(X x);

契約プログラミングでは非常に便利です:

  • 動作 (f の本体) を課すため、および
  • ローカル コンテキスト (x) を提供します。

多くの場合、動作は状態を保持することによって実現されます (あなたの のようにinitiated)。はい、遅延評価には問題ありません。怠惰な評価は、フィールド レベルで、たとえばめったに見られない宝石によって達成できますがFuture<>

于 2012-11-26T10:50:46.547 に答える
0

DumbComputer2番目の例は、を呼び出すたびに新しいものを作成するため、(おそらく)意図したとおりに機能しませんThunk.get。次のように目標を達成できます(ただし、これは優れた設計ではないと思います。また、より簡単なソリューションと比較した場合の利点がどこにあるのかわかりません)

abstract class Thunk<T> {

    T value;
    Computer<T> computer;

    protected abstract T doCompute ();

    private interface Computer<T> {
        Computer getComputer ();
        T compute ();
    }

    public Thunk<T> () {
        // initialize computer with a calculating one
        computer = new Computer<T> () {
            Computer getComputer () {
                // return a dumb computer
                return new Computer<T> () {
                    Computer getComputer () { return this; }
                    T compute () { return value; }
                }
            }
            T compute () { value = doCompute (); return value; }
        };
    }

    public T getValue () {
        T v = computer.compute (); computer = computer.getComputer (); return v;
    }
}
于 2012-11-25T08:41:47.217 に答える