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; 
    } 

} 

이 빌더 패턴으로 사용될 수 있으며,이 구축 된 후 각 속성 만 설정 될 수 있기 때문에, 그것은, 효과적으로 불변입니다 일단. 즉 :

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

그러나, Pizza는 스레드로부터 안전하지 않습니다 : 보장이없는 스레드 B가 문제를 해결하는 몇 가지 방법이 있습니다 스레드 A.에 의해 그것으로 설정 한 성분을보고 있다는 :

  • 회원 만들기 final. 하지만 빌더 패턴을 사용할 수는 없습니다.
  • 회원에 대한 액세스를 동기화하십시오. 그러나 이것은 단 한 번만 기록되기 때문에 낭비처럼 보입니다.
  • volatile으로 지정하십시오. 동기화와 같은 낭비를 느낍니다.
  • AtomicReference을 사용하십시오.
  • 등?

내 질문은 어떤 방법을 호출 한 후에 클래스 구성원이 변경되지 않는다는 것을 JVM에 알리는 가장 좋은 방법은 무엇입니까? 방금 액세스 권한을 동기화하고 JVM이 잠금을 최적화 할 것이라고 신뢰해야합니까? 내가 을 알고 있기 때문에 을 알고 있기 때문에 final이 설정된 후처럼 동작해야하므로 낭비라고 느껴집니다. 더 나은 해결책이 없습니까?

답변

5

빌더 패턴은 일반적으로 빌더가 개별 오브젝트임을 의미합니다. 이 경우 개체의 필드 final를 건설되고 만들 수 있고, 빌더 객체에 의해 호출 생성자를 초기화 :

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

또는, 당신은 Pizza 객체의 안전 간행물을 보장 할 수 있습니다. 안전한 게시 관용구는 게시되는 개체에 대한 참조를 포함하는 필드에 적용되며 개체 자체의 필드에는 적용되지 않습니다. 예를 들어 pizza이 일부 개체의 필드 인 경우 volatile으로 만들거나 해당 개체에 대한 액세스를 동기화 할 수 있습니다. Pizza 개체가 해당 필드에 안전하게 게시되도록 할 수 있습니다.

1

구성원 값을 변경할 의도가없는 경우 더 좋은 패턴은 매개 변수로 IngredientsPrice을 취하고 해당 개체에 대한 설정 메서드가 전혀없는 Pizza 생성자를 갖는 것입니다. 실제로 처음 호출 한 후에 예외를 throw하는 setSomething() 메서드를 사용하는 것은 유용하지 않습니다.

String 클래스의 작동 원리를 고려하십시오. 일부 텍스트로 String을 인스턴스화하면 텍스트 값을 변경할 수 없습니다. 다른 값을 가진 String을 얻는 유일한 방법은 새 값을 만드는 것입니다. 그것이 당신이 여기에서 원하는 것 같아 보입니다.

이 패턴을 사용하면 동기화 문제가 발생하지 않습니다.

+0

예외가 처음 호출 된 후 예외를 throw하는'setSomething()'메소드의 아이디어는 해당 멤버의 효과적인 불변 속성을 적용하는 것이다.물론 모든 것이 '최종'일 수 있으며, 20 개의 인수를 사용하는 생성자가 있습니다.하지만이를 피하는 것이 AFAIK 빌더 패턴의 한 포인트입니다. –

+1

하지만이 경우에는 20 개가 아니라 2 개의 인수 만 있습니다. 걱정이된다면 axtavt의 대답을 제안하십시오. _always_ 작동하는 setter를 가진'PizzaBuilder' 클래스를 가질 수 있으며'build()'(또는 개인적으로'toPizza()'를 사용할 때까지'Pizza' 인스턴스가 생성되지 않습니다. 그래서'Pizza' 생성자에 대해 알아야 할 유일한 곳은'PizzaBuilder'입니다. 다른 모든 사람들은''Pizza'를 얻기 위해서는''PizzaBuilder''를 사용해야합니다. – aroth