1

次のクラス:

class Pizza {

    Ingredients ingredients;
    Price price;

    public setIngredients(Ingredients ing) {
        if (ingredients != null) {
            throw new IllegalStateException();
        }
        ingredients = ing;
        return this;
    }

    public setPrice(Price p) {
        if (price != null) {
            throw new IllegalStateException();
        }
        price = p;
        return this;
    }

}

ビルダーパターンで使用できます。各プロパティは1回しか設定できないため、ビルド後は事実上不変です。あれは:

Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);

ただし、Pizzaスレッドセーフではありません。スレッドBがスレッドAによって設定された成分を認識できるという保証はありません。これを修正する方法はいくつかあります。

  • メンバーを作成しfinalます。ただし、ビルダーパターンを使用することはできません。
  • メンバーへのアクセスを同期します。しかし、これは無駄のように思えます。なぜなら、それらは一度だけ書かれているからです。
  • それらを作成しますvolatile。同期のように無駄を感じます。
  • を使用しAtomicReferenceます。
  • 等。?

私の質問は、何らかのメソッドが呼び出された後、クラスメンバーが変更されないことをJVMに伝える最良の方法は何ですか?アクセスを同期して、JVMがロックを最適化することを信頼する必要がありますか?メンバーは設定後のように振る舞うべきだと私は知っているので、それはただ無駄に感じます。より良い解決策はありませんか?final

4

2 に答える 2

5

Builder パターンは通常、builder が別のオブジェクトであることを意味します。この場合、ビルド中のオブジェクトのフィールドを作成finalし、ビルダー オブジェクトによって呼び出されるコンストラクターでそれらを初期化できます。

Pizza pizza = 
    new PizzaBuilder()
        .setIngredients(something)
        .setPrice(somethingelse)
        .build(); 

または、オブジェクトを安全に公開することもできPizzaます。安全な公開イディオムは、公開されるオブジェクト自体のフィールドではなく、そのオブジェクトへの参照を含むフィールドに適用されることに注意してください。たとえば、pizzaがあるオブジェクトのフィールドである場合、それを作成したり、アクセスを同期したりできます。これにより、そのフィールドに割り当てられたオブジェクトvolatileの安全な公開が保証されます。Pizza

于 2011-03-21T11:16:22.820 に答える
1

メンバー値が変更されることを意図していない場合、より良いパターンは、パラメーターとを取り、オブジェクトにセッターメソッドをまったく持たないPizzaコンストラクターを持つことです。実際には、最初に呼び出された後に例外をスローするメソッドがあると便利ではありません。IngredientsPricesetSomething()

Stringクラスがどのように機能するかを検討してください。あるテキストでインスタンス化したString後は、テキスト値を変更することはできません。異なる値でを取得する唯一の方法Stringは、新しい値を作成することです。それがあなたがここで望んでいることのようです。

このパターンを使用すると、同期の問題も回避されます。

于 2011-03-21T11:08:48.840 に答える