Builder Pattern이란?
생성자를 통해 객체를 생성하는데 있어 존재하는 단점을 피하고자 별도의 Builder 클래스를 사용해 객체를 생성하는 방식
* 생성자를 통한 객체 생성
Bag bag = new Bag("name", 1000, "memo");
*빌더 패턴을 사용한 객체 생성
Bag bag = Bag.builder()
.name("name")
.money(1000)
.memo("memo")
.build();
왜 사용?
가독성 증대
Setter 없으므로 변경 불가능한 객체 생성 가능
한번에 객체를 반환하므로 객체 일관성 유지 가능
필수 인자 지정 가능
Null 이 들어가는 값에 대해 명시적으로 표현하지 않아도 됨
* 기존 생성자를 활용할 경우
Person person = new Person("John", 25, null, null, null);
각 인자가 무슨 데이터를 의미하는지 알기 쉽지 않을 뿐더러 3~5번째 인자는 값이 필요 없음에도 null이라는 값을 넣어서 생성해야 함
* 빌더 패턴을 활용할 경우
Person person = Person.builder()
.name("John")
.age(25)
.build();
데이터가 필요없는 값은 따로 null을 주지 않아도 됨
John은 name, 25는 age라는 데이터를 의미한다는 것을 쉽게 확인 가능
빌더 패턴 구현
public class Person {
private String name;
private int age;
private String gender;
private String job;
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String getGender(){
return gender;
}
public String getJob(){
return job;
}
Person(Builder builder){
}
public static class Builder{
private String name;
private int age;
private String gender;
private String job;
public Builder(String name){
this.name = name;
}
public Builder age(int age){
this.age = age;
return this;
}
public Builder gender(String gender){
this.gender = gender;
return this;
}
public Builder job(String job){
this.job = job;
return this;
}
public Person build(){
return new Person(this);
}
}
}
Person 클래스에 빌더패턴을 위한 Builder 클래스 구현
Person person = new Builder("John")
.age(25)
.gender("male")
.job("programmer")
.build();
위와 같이 빌더패턴으로 객체 생성 가능
@Builder
lombok에서 제공하는 어노테이션
위와 같은 Builder클래스를 직접 구현하지 않고도 Builder pattern 사용 가능
@Builder
public class Person {
private String name;
private int age;
private String gender;
private String job;
}
@Builder 어노테이션을 붙이기만 하면
Person person = Person.builder()
.name("John")
.age(25)
.gender("male")
.job("programmer")
.build()
위와 같이 빌더패턴 사용 가능
@Builder 옵션
builderMethodName
@Builder 어노테이션의 빌더 생성하는 메소드의 기본 이름은 builder
이 이름을 변경할 수 있는 옵션
@Builder(builderMethodName = "builder")
buildMethodName
builder 메소드로 얻은 빌더에 필드 값을 삽입하고 마지막에 객체를 생성하는 메소드인 build 메소드의 이름을 변경할 수 있는 옵션
@Builder(builderMethodName = "builder", buildMethodName = "build")
builderClassName
내부의 Builder 클래스의 이름을 변경하는 옵션
@Builder(builderMethodName = "builder", buildMethodName = "build", builderClassName = "builderClass")
내부 Builder 클래스 이름의 경우 바꿀 필요가 없을 것 같은데 왜 사용?
public class User {
private String userId;
private String password;
private String email;
private Long point;
private String hobby;
@Builder(builderMethodName = "userBuilder")
private User(String userId, String password, String email) {
this.userId = userId;
this.password = password;
this.email = email;
}
@Builder(builderMethodName = "anonymousBuilder")
private User(String userId, String password) {
this.userId = userId;
this.password = password;
}
@Builder(builderMethodName = "allBuilder")
private User(String userId, String password, String email, Long point, String hobby) {
this.userId = userId;
this.password = password;
this.email = email;
this.point = point;
this.hobby = hobby;
}
@Builder(builderMethodName = "updateBuilder")
private User(Long point) {
this.point = point;
}
}
위와 같이 2개 이상의 생성자에 @Builder 를 사용하는 경우
내부 Builder 클래스의 이름이 같기 때문에 하나의 @Builder만 적용
이를 해결하기 위해 builderClassName, builderMethodName 함께 사용
builderMethodName
boolean 값으로 설정할 수 있는 어노테이션
이 값을 true로 설정 시 builder로 만든 인스턴스에서 toBuilder() 메소드를 호출하여 그 인스턴스의 값을 베이스로 빌더 패턴을 만들어 새로운 인스턴스 생성 가능
@Builder(toBuilder = true)
public class Person {
private String name;
private int age;
private String gender;
private String job;
}
위와 같은 클래스에서 toBuilder = true 로 설정하게 되면
Person firstPerson = Person.builder().name("john").age(25).job("programmer").build();
Person secPerson = firstPerson.toBuilder().name("Dave").build();
이름이 John, 나이가 25, 직업이 programmer인 firstPerson 객체를 생성한 후
나이, 직업은 위와 동일하지만 이름은 Dave로 다른 새로운 객체인 secPerson을 손쉽게 생성 가능
access
Builder의 접근 제한자를 설정하는 옵션
@Builder(access = AccessLevel.PRIVATE)