하지만, 자바빈즈 패턴에서는 객체 하나를 만드려면 메서드를 여러 개 호출해야하고, 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 있게 된다. 일관성이 깨지므로 자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없으며, 스레드 안정성을 얻으려면 개발자가 추가 작업을 해줘야한다. 이러한 단점을 보완하기 위해 freeze 메서드를 사용할 수 있으나, freeze 메서드를 확실히 호출해줬는지 컴파일러가 보증할 방법이 없어 런타임 오류에 취약하다.
클라이언트는 필수 매개변수만으로 생성자를 호출해 빌더 객체를 얻고, 빌더 객체가 제공하는 setter 메서드들로 원하는 선택 매개변수들을 설정할 수 있다. 마지막으로 매개변수가 없는 build() 메서드를 호출해 필요한 객체를 얻을 수 있다. 이렇게 연쇄적으로 메서드를 호출하는 방법을 fluent API or method chaining이라 한다.
불변 : 어떠한 변경도 허용하지 않는다. 대표적으로 String 객체는 한번 만들어지면 절대 값을 바꿀 수 없는 불변 객체
불변식 : 프로그램이 실행되는 동안(혹은 정해진 기간) 반드시 만족해야하는 조건을 말한다. 변경을 허용할 수 는 잇으나, 주어진 조건 내에서만 허용한다는 뜻이다.
불변식을 보장하기 위해서는 빌더로 부터 매개변수를 복사한 후 해당 객체 필드도 검사해야한다.(item50) 검사시 잘못된 점을 발견하면 어떤 매개변수가 잘못되었는지에 대한 메세지를 담아 IllegalArgumentException (item75) 오류 발생을 해주면된다.
Gmarket gmarket = new Gmarket.Builder(Gmarket.Chnl.MART).addApiType(Gmarket.ApiType.UPDATE_ITEM).addApiType(Gmarket.ApiType.UPDATE_PRC).build();
Naver naver =new Naver.Builder().addApiType(Naver.ApiType.ADD_ITEM).connectToHapi().build();
각각의 하위 클래스의 빌더가 정의한 build 메서드는 해당 하위 클래스(Naver, Gmarket)을 반환하도록 되어있다. 이렇게 하위 클래스의 메서드가 상위 클래스가 정의한 리턴 타입이 아닌, 그 하위 타입을 리턴하는 것을 Convariant return typing(공변 반환 타이핑)이라 한다. 이 기능으로 클라이언트가 형변환에 신경 쓰지 않고 빌더를 사용할 수 있다.
결론
빌더 패턴은 빌더 하나로 여러 객체를 만들 수 있고, 빌더에 넘기는 매개변수에 따라 다른 객체를 만들 수 있으므로 매우 유연하다.
하지만 객체를 만들려면, 그에 앞서 빌더부터 만들어야한다. 또한, 성능에 민감한 상황에서는 빌더 생성 비용이 문제가 될 수 있다. 또한 매개변수가 4개 이상이 되어야 값어치를 한다.
즉, 인자가 많은 생성자나 정적 팩터리가 필요한 클래스를 설계할 때, 대부분의 인자가 선택적 인자인 상황에 유용하다. 빌더는 점층적 생성자보다 간결하고, 자바빈즈보다 훨씬 안전하다.