Garantire la visibilità della memoria con il modello Builder/Factory
-
28-10-2019 - |
Domanda
La seguente classe:
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;
}
}
potrebbe essere usato in un modello di costruttore e dopo che è stato costruito, è efficacemente immutabile, perché ogni proprietà può essere impostata una sola volta. Questo è:
Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);
Tuttavia, Pizza
non è sicuro: non ci sono garanzie che il filo B veda gli ingredienti che sono stati impostati dal thread A. Ci sono alcuni modi per risolverlo:
- Fare membri
final
. Ma poi non puoi usare un modello di costruttore. - Sincronizzare l'accesso ai membri. Ma questo sembra spreco, perché sono scritti solo una volta.
- Falli
volatile
. Si sente spreco, come la sincronizzazione. - Uso
AtomicReference
. - Eccetera.?
La mia domanda è: qual è il modo migliore per dire al JVM che un membro della classe non cambierà dopo che è stato chiamato qualche metodo? Dovrei semplicemente sincronizzare l'accesso ad esso e fidarmi che JVM ottimizzerà il blocco? Sembra solo sprecato, perché io sapere che il membro dovrebbe comportarsi come è final
dopo che è stato impostato. Non ci sono soluzioni migliori?
Soluzione
Il modello Builder di solito significa che il costruttore è un oggetto separato. In questo caso puoi creare campi dell'oggetto in costruzione final
, e inizializzali nel costruttore chiamato dall'oggetto Builder:
Pizza pizza =
new PizzaBuilder()
.setIngredients(something)
.setPrice(somethingelse)
.build();
In alternativa, è possibile garantire una pubblicazione sicura del Pizza
oggetto. Si noti che i idiomi di pubblicazione sicuri sono applicati al campo che contiene un riferimento all'oggetto pubblicato, non ai campi di quell'oggetto stesso. Ad esempio, se pizza
è un campo di un oggetto, puoi farcela volatile
o sincronizzare l'accesso ad esso: garantirebbe una pubblicazione sicura di Pizza
oggetto assegnato a quel campo.
Altri suggerimenti
Se i valori dei membri non dovrebbero mai cambiare, il modello migliore sarebbe avere un Pizza
costruttore che prende come parametri Ingredients
e Price
, e non hanno alcun metodo di setter sull'oggetto. Davvero non è utile avere un setSomething()
Metodo che lancia un'eccezione dopo la prima volta che viene chiamato.
Considera come il String
La classe funziona. Una volta che hai istanziato a String
Con un po 'di testo, il valore del testo non può essere modificato. L'unico modo per ottenere un String
con un valore diverso è costruirne uno nuovo. Sembra che sia quello che vuoi qui.
L'uso di questo modello evita anche il problema della sincronizzazione.