생성자와 정적 팩토리 메서드 패턴을 이용해서 인스턴스를 생성할 경우, 매개변수가 너무 많아지면 코드가 길어지고 매개변수의 파악이 어려워 실수할 가능성이 있다.
예를 들어 해외 여행 계획을 짜기 위한 클래스가 있을 때, 필수 매개변수로는 항공권, 숙소, 선택 변수로 관광지 가기, 문화 체험하기 등이 선택 변수로 있을 것이다. 이런 경우 텔리스코핑생성자 패턴(점층적 생성자 패턴) 을 사용해서 객체를 만들어주었다. 텔리스코핑 생성자 패턴은 필수 매개변수를 갖는 생성자에 선택 매개변수를 더하는 방식이다.
필수 매개변수 생성자
필수 매개변수 + 선택 매개변수 1 생성자
필수 매개변수 + 선택 매개변수 2 생성자
필수 매개변수 + 선택 매개변수 1 + 선택 매개변수 2 생성자
...
class Travel {
String airlineTicket;
String accommodation;
String touristAttraction;
String culturalExperience;
public Travel(String airlineTicket, String accommodation, String touristAttraction,
String culturalExperience) {
this.airlineTicket = airlineTicket;
this.accommodation = accommodation;
this.touristAttraction = touristAttraction;
this.culturalExperience = culturalExperience;
}
public Travel(String airlineTicket, String accommodation, String touristAttraction) {
this(airlineTicket, accommodation, touristAttraction, null);
}
public Travel(String airlineTicket, String accommodation) {
this(airlineTicket, accommodation, null, null);
}
}
코드에서 볼 수 있듯, 매개변수의 타입이 같은 경우 선택 필드를 구분하기 어렵고, 컬럼이 너무 많아지면 가독성이 떨어지는 등의 관리의 어려움도 있다.
이렇게 선택 매개변수가 너무 많을 경우 자바빈즈를 이용하여 해결할 수도 있다.
기본 생성자를 만들고 세터 매서드를 이용하는 방법이다.
public Travel() {}
public void setAirlineTicket(String airlineTicket) {
this.airlineTicket = airlineTicket;
}
public void setAccommodation(String accommodation) {
this.accommodation = accommodation;
}
public void setTouristAttraction(String touristAttraction) {
this.touristAttraction = touristAttraction;
}
public void setCulturalExperience(String culturalExperience) {
this.culturalExperience = culturalExperience;
}
코드가 직관적이고 인스턴스 생성이 쉬운 장점이 있지만, 여러번의 메서드를 사용하기 때문에 자바 빈 객체가 일관성을 유지하기 어려울 가능성이 매우 높다는 단점이 있다. 데이터의 가공이 쉽기 때문에, 유효성을 확인하기 어렵고, 데이터의 문제가 발생했을 때 처리함에 있어 매우 어려운 일이 발생할 수 있다.
빌더 패턴은 다음과 같은 방법으로 사용된다.
1. 원하는 객체를 바로 생성하되, 모든 필수 매개변수를 갖는 생성자를 호출하여 빌더 객체를 얻는다.
2. 빌더 객체의 세터 메서드를 호출하여 선택 매개변수의 값을 설정한다.
3. 매개변수가 없는 build 메서드를 호출하여 불변 객체를 생성한다.
class Travel {
String airlineTicket;
String accommodation;
String touristAttraction;
String culturalExperience;
public static class Builder {
String airlineTicket;
String accommodation;
String touristAttraction;
String culturalExperience;
public Builder airlineTicket(String airlineTicket) {
this.airlineTicket = airlineTicket;
return this;
}
// 1. -2 : 빌더 객체로 리턴하는 setter 만든다.
public Builder accommodation(String accommodation) {
this.accommodation = accommodation;
return this;
}
public Builder touristAttraction(String touristAttraction) {
this.touristAttraction = touristAttraction;
return this;
}
public Builder culturalExperience(String culturalExperience) {
this.culturalExperience = culturalExperience;
return this;
}
// 3. 불변 객체 생성하는 메서드
public Travel build() {
return new Travel(this);
}
}
// 1. builder 객체를 매개변수로 받는 생성자
public Travel(Builder builder) {
this.airlineTicket = builder.airlineTicket;
this.accommodation = builder.accommodation;
this.touristAttraction = builder.touristAttraction;
this.culturalExperience = builder.culturalExperience;
}
public static void main(String[] args) {
Travel t = new Builder()
.airlineTicket("KE")
.accommodation("airbnb")
.build();
}
}
요즘은 lombok의 @bulider를 통해 구현하지 않고 사용한다.
빌더패턴은 유연성이 좋고, 다양한 매개변수들의 사용가능하다.
또한 훌륭한 추상 팩토리를 만드는데, 빌더라는 타입만을 이용해서 모든 빌더에 대해 하나의 제네릭만 만든다면 다양하게 사용이 가능하다.
그치만 텔리스코핑 패턴보다 변수가 길어질 수 있기 때문에, 매개변수가 적어도 4개 이상인 경우에 사용하는 것이 좋다.
effective java