Garantizar la visibilidad de la memoria con el constructor/patrón de fábrica
-
28-10-2019 - |
Pregunta
La siguiente clase:
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;
}
}
podría usarse en un patrón de constructor, y después de que se haya construido, es efectivamente inmutable, porque cada propiedad se puede configurar solo una vez. Eso es:
Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);
Sin embargo, Pizza
no es seguro del hilo: no hay garantías de que el hilo B ve los ingredientes que el hilo colocó en él A. Hay algunas formas de solucionarlo:
- Hacer miembros
final
. Pero entonces no puedes usar un patrón de constructor. - Sincronice el acceso a los miembros. Pero esto parece desperdicio, porque están escritos solo una vez.
- Hazlos
volatile
. Se siente residuos, como la sincronización. - Usar
AtomicReference
. - Etc.?
Mi pregunta es, ¿cuál es la mejor manera de decirle al JVM que un miembro de la clase no cambiará después de que se haya llamado a algún método? ¿Debo sincronizar el acceso a él y confiar en que el JVM optimizará el bloqueo? Simplemente se siente desperdicio, porque yo saber que el miembro debería comportarse como es final
Después de que esté listo. ¿No hay mejores soluciones?
Solución
El patrón de constructor generalmente significa que el constructor es un objeto separado. En este caso, puede hacer campos del objeto que se está construyendo final
, e inicializarlos en el constructor llamado por el objeto Builder:
Pizza pizza =
new PizzaBuilder()
.setIngredients(something)
.setPrice(somethingelse)
.build();
Alternativamente, puede garantizar una publicación segura del Pizza
objeto. Tenga en cuenta que los modismos de publicación seguros se aplican al campo que contiene una referencia al objeto que se publica, no a los campos de ese objeto en sí. Por ejemplo, si pizza
es un campo de algún objeto, puedes hacerlo volatile
o sincronizar el acceso a él: garantizaría una publicación segura de Pizza
objeto asignado a ese campo.
Otros consejos
Si los valores de los miembros nunca pretenden cambiar, el mejor patrón sería tener un Pizza
constructor que toma como sus parámetros Ingredients
y Price
, y no tienen métodos establecidos en el objeto en absoluto. Realmente no es útil tener un setSomething()
Método que arroja una excepción después de la primera vez que se llama.
Considere cómo el String
clase funciona. Una vez que hayas instanciado un String
Con algún texto, el valor del texto no se puede cambiar. La única forma de obtener un String
con un valor diferente es construir uno nuevo. Parece que eso es lo que quieres aquí.
El uso de este patrón también evita el problema de sincronización.