Обеспечение видимости памяти с помощью компоновщика / фабричного шаблона
Следующий класс:
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
после того, как это установлено. Нет ли лучших решений?
2 ответа
Образец строителя обычно означает, что строитель является отдельным объектом. В этом случае вы можете сделать поля строящегося объекта final
и инициализировать их в конструкторе, вызванном объектом builder:
Pizza pizza =
new PizzaBuilder()
.setIngredients(something)
.setPrice(somethingelse)
.build();
Кроме того, вы можете обеспечить безопасную публикацию Pizza
объект. Обратите внимание, что идиомы безопасной публикации применяются к полю, содержащему ссылку на публикуемый объект, а не к полям самого объекта. Например, если pizza
это поле какого-то объекта, вы можете сделать это volatile
или синхронизировать доступ к нему - это обеспечит безопасную публикацию Pizza
объект, присвоенный этому полю.
Если значения элементов никогда не должны изменяться, то лучшим вариантом будет иметь Pizza
конструктор, который принимает в качестве своих параметров Ingredients
а также Price
и вообще не имеют методов установки объекта. На самом деле это не полезно иметь setSomething()
метод, который выбрасывает исключение после первого вызова.
Рассмотрим, как String
классные работы. Как только вы создали экземпляр String
с некоторым текстом текстовое значение не может быть изменено. Единственный способ получить String
с другим значением, чтобы построить новое. Кажется, это то, что вы хотите здесь.
Использование этого шаблона также позволяет избежать проблемы синхронизации.