jvm lang/Effective Java
20190616 Item 2. 생성자에 매개변수가 많다면 빌더를 고려하라.
doubler
2019. 6. 16. 22:33
만약 객체의 매개변수가 많다면?
- 생성자 대신 정적 팩토리 메서드를 이용하였지만 매개변수가 많게 된다면, 그에 따라 팩토리 메서드도 늘어날 것이다. 생성자는 어떠한가? 생성자 또한 받는 매개변수의 개수만큼 늘어날 것이다.
- 위의 대한 해결책으로 나온 것이 빌더(Builder)이다. 디자인 패턴의 빌더 패턴의 그 빌더이다.
예시를 바로 보자.
- Account 객체가 존재한다.
- 해당 객체에는 필수적으로 들어가는 필드 값과 선택적으로 들어가는 필드 값이 존재한다.
public class Account {
/** 필수 필드 **/
private final String name; /** 이름 (필수) **/
private final Integer age; /** 나이 (필수) **/
private final String email; /** 메일 (필수) **/
/** 선택 필드 **/
private final String company; /** 회사 (선택) **/
private final String phoneNumber; /** 휴대폰번호 (선택) **/
private final String introduce; /** 자기소개 (선택) **/
}
점층적 생성자 패턴 샘플 코드
- 점층적 생성자 패턴을 사용하게 되면 클라이언트 코드를 읽기 어렵다. 하나의 메서드 내에 들어가는 매개변수의 개수가 다양하기 때문이다.
public Account(String name, Integer age, String email){
this(name, age, email, "empty");
}
public Account(String name, Integer age, String email, String company){
this(name, age, email, company, "empty");
}
public Account(String name, Integer age, String email, String company, String phoneNumber){
this(name, age, email, company, phoneNumber, "empty");
}
public Account(String name, Integer age, String email,
String company, String phoneNumber, String introduce){
this.name = name;
this.age = age;
this.email = email;
this.company = company;
this.phoneNumber = phoneNumber;
this.introduce = introduce;
}
자바 빈즈 패턴 샘플 코드
- 생성자는 매개변수를 받지 않는다. 다만 setter() 메서드를 필드 개수만큼 만든다.
- 객체를 만들기 위해선 각각의 setter() 메소드를 호출해야 한다. 이렇게 된다면 객체의 생성 이후 완전해지기 이전에 일관성이 깨지는 문제가 발생한다. 일관성이 깨진다는 것은 멀티스레드 환경에서 스레드 안전성을 위해 프로그래머가 추가적인 작업을 해야함을 의미한다.
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setEmail(String email) {
this.email = email;
}
public void setCompany(String company) {
this.company = company;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
빌더패턴 샘플 코드
- 점층적 생성자 패턴 + 자바 빈즈 패턴
- 필요한 값들로만 구성된 객체를 획득할 수 있다.
public class Account {
/** 필수 필드 **/
private final String name; /** 이름 (필수) **/
private final Integer age; /** 나이 (필수) **/
private final String email; /** 메일 (필수) **/
/** 선택 필드 **/
private final String company; /** 회사 (선택) **/
private final String phoneNumber; /** 휴대폰번호 (선택) **/
private final String introduce; /** 자기소개 (선택) **/
public static class Builder{
/** 필수 필드 **/
private String name; /** 이름 (필수) **/
private Integer age; /** 나이 (필수) **/
private String email; /** 메일 (필수) **/
/** 선택 필드 **/
private String company = "empty"; /** 회사 (선택) **/
private String phoneNumber = "empty"; /** 휴대폰번호 (선택) **/
private String introduce = "empty"; /** 자기소개 (선택) **/
public Builder(String name, Integer age, String email){
this.name = name;
this.age = age;
this.email = email;
}
public Builder company(String company){
this.company = company;
return this;
}
public Builder phoneNumber(String phoneNumber){
this.phoneNumber = phoneNumber;
return this;
}
public Builder introduce(String introduce){
this.introduce = introduce;
return this;
}
public Account build(){
return new Account(this);
}
}
private Account(Builder builder){
this.name = builder.name;
this.age = builder.age;
this.email = builder.email;
this.company = builder.company;
this.phoneNumber = builder.phoneNumber;
this.introduce = builder.introduce;
}
}
실행 샘플 코드
Account account = new Account
.Builder("홍길동", 55, "hong-gildong@gmail.com")
.company("홍길동주식회사")
.build();
위와 같이 Account 객체를 생성하기 위해서 빌더배턴을 이용함을 볼 수 있다. 연쇄적으로 메소드를 호출하고 있기 때문에 위와 같은 코드를 플루언트 API 혹은 메소드 체이닝(method chaning) 이라고 부른다.
원래는 빌더패턴을 구현하기 위해선 객체 각각에 빌더를 만들어야 하지만 롬복 라이브러리가 등장하면서 따로 빌더를 작성하지 않더라도 @Builder 어노테이션만 해당 생성자에 붙이면 알아서 코드를 작성해준다.
reference