ビルダー/工場パターンによるメモリの可視性を確保します
-
28-10-2019 - |
質問
次のクラス:
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;
}
}
ビルダーパターンで使用でき、構築された後、 効果的に不変, 、各プロパティは一度だけ設定できるためです。あれは:
Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);
でも、 Pizza
スレッドは安全ではありません:スレッドBがスレッドAによって設定された成分を見るという保証はありません。それを修正する方法はいくつかあります。
- メンバーを作成します
final
. 。ただし、ビルダーパターンを使用することはできません。 - メンバーへのアクセスを同期します。しかし、これは無駄のように思えます。なぜなら、彼らは一度だけ書かれているからです。
- それらを作ります
volatile
. 。同期のような無駄を感じます。 - 使用する
AtomicReference
. - 等。?
私の質問は、何らかの方法が呼び出された後にクラスのメンバーが変わらないことをJVMに伝える最良の方法は何ですか?アクセスを同期するだけで、JVMがロックを最適化することを信頼する必要がありますか?私はただ無駄を感じています 知る メンバー したほうがいい そのように振る舞います final
設定された後。より良い解決策はありませんか?
解決
ビルダーパターンは通常、ビルダーが別のオブジェクトであることを意味します。この場合、構築されているオブジェクトのフィールドを作成できます final
, 、およびビルダーオブジェクトで呼ばれるコンストラクターでそれらを初期化します。
Pizza pizza =
new PizzaBuilder()
.setIngredients(something)
.setPrice(somethingelse)
.build();
または、の安全な公開を確保することができます Pizza
物体。安全な出版物イディオムは、そのオブジェクト自体のフィールドではなく、公開されているオブジェクトへの参照を含むフィールドに適用されることに注意してください。たとえば、if pizza
いくつかのオブジェクトのフィールドです、あなたはそれを作ることができます volatile
またはそれへのアクセスを同期する - それはの安全な公開を保証するでしょう Pizza
そのフィールドに割り当てられたオブジェクト。
他のヒント
メンバー値が変更されることを意味しない場合、より良いパターンは Pizza
パラメーターとして取得するコンストラクター Ingredients
と Price
, 、そしてオブジェクトにセッターメソッドがまったくありません。本当にありません setSomething()
初めて呼び出された後に例外をスローする方法。
方法を考えてください String
クラスの作品。インスタンス化したらa String
いくつかのテキストでは、テキスト値を変更できません。取得する唯一の方法 String
異なる値では、新しい値を構築することです。それがあなたがここに望むもののようです。
このパターンを使用すると、同期の問題も回避されます。