빌더 패턴은 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴이다.
Member member = new Member("홍길동", "id", "pwd", "test@email.com");
생성자를 통해 객체를 생성할 경우 여러가지 단점이 있다.
생성자 파라미터가 많을 경우 가독성이 좋지 않다.
생성자 파라미터의 순서를 반드시 지켜야한다.
생성자에 설정하지 않는 값들은 전부 null로 채워져야 한다.
public class Member {
private String name;
private String id;
private String pwd;
private Member(String name, String id, String pwd){
this.name = name;
this.id = id;
this.pwd = pwd;
}
public static Builder builder(){
return new Builder();
}
public static class Builder{
private String name;
private String id;
private String pwd;
public Builder name(String name){
this.name = name;
return this;
}
public Builder id(String id){
this.id = id;
return this;
}
public Builder pwd(String pwd){
this.pwd = pwd;
return this;
}
public Member build(){
return new Member(name, id, pwd);
}
}
}
// builder 패턴 사용 코드
public class Main {
public static void main(String[] args) {
Member member = Member.builder()
.name("name")
.id("id")
.pwd("pwd")
.build();
}
}
빌더패턴을 사용할 경우 가독성도 좋아질 뿐만 아니라 순서의 상관없이 파라미터를 설정할 수 있다.
메서드의 값을 넣지 않을 경우 null 값이 들어간다.
롬복에서 지원하는 @Builder 어노테이션을 사용하면, 간단하게 빌더를 사용할 수 있다.
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Member {
private String name;
private String id;
private String pwd;
}
Member member = Member.builder()
.name("name")
.id("id")
.pwd("pwd")
.build();
클래스의 @Builder 어노테이션을 사용하면 해당 클래스의 모든 필드를 빌더 메서드로 사용가능 하다.
@NoArgsConstructor
@AllArgsConstructor
클래스의 @Builder 어노테이션을 사용할 경우 @NoArgsConstructor, @AllArgsConstructor 어노테이션을 함께 사용해야 한다.
public class Member {
private String name;
private String id;
private String pwd;
@Builder
public Member(String id, String pwd){
this.id = id;
this.pwd = pwd;
}
Member member = Member.builder()
.id("id")
.pwd("pwd")
.build();
생성자에서 사용하게 되면 지정한 값만 메서드를 사용 가능하게 된다.
builderMethodName
@Builder 어노테이션을 사용하면 빌더를 생성하는 메서드의 이름은 기본 값인 builder()이다. 이를 새롭게 네이밍 할 수 있는 어노테이션 속성이다. (Default : builder())
builderClassName
@Builder를 사용하면 각 필드들의 값을 셋팅하는 메서드의 반환값의 빌더 클래스의 이름을 ...Builder로 자동 설정된다. 예를 들어 클래스 이름이 Member일 경우 @Builder의 적용된 반환 값이 MemberBuilder가 된다. 이걸 사용자가 원하는 대로 네이밍 하고 싶을때 builderClassName
속성을 사용한다.
toBuilder
boolean 값으로 설정할 수 있는 어노테이션으로 기본 값은 false이다. 이 값을 true로 설정 시 빌더로 만든 인스턴스에서 toBuilder() 메서드를 호출해 그 인스턴스 값을 베이스로 빌더 패턴으로 새로운 인스턴스를 생성할 수 있다. 기본값인 fasle로 설정할 경우 toBuilder() 메서드 자체가 존재하지 않을 경우 에러가 발생한다.
Member member1 = Member.builder()
.name("홍길동")
.id("id")
.pwd("pwd")
.email("test@email.com")
.build();
Member member2 = Member.toBuilder().name("홍길동").build();
access()
builder 클래스의 접근제어자를 어떻게 할 것인지에 대한 설정 값이다. (Default : public)
AccessLevel access() default lombok.AccessLevel.PUBLIC;
@Builder
@ToString
public class Member {
@Builder.Default private final String name = "name";
private String id;
private String pwd;
}
Member member = Member.builder()
.id("id")
.pwd("pwd")
.build();
위 코드처럼 name 메서드에 값을 지정해주지 않으면 name 에는 null 값이 들어간다. 하지만 @Builder.Default 어노테이션으로 초기값을 지정해주면 "name" 값이 초기값으로 설정된다.
하지만 @Builder.Default 어노테이션을 사용하지 않고 값을 넣어도 결과는 똑같이 출력 된는데 음... 이건 다시 공부해야겠다...
@Builder
@ToString
public class Member {
private String name;
private String id;
private String pwd;
@Singular private List<String> hobbies;
}
Member member = Member.builder()
.name("name")
.id("id")
.pwd("pwd")
// 값을 메서드로 하나 하나씩 넘긴다.
.hobby("운동")
.hobby("독서")
.hobby("잠")
.build();
System.out.println(member);
// 출력 결과: Member(name=name, id=id, pwd=pwd, hobbies=[운동, 독서, 잠])
상속 관계에서 빌더 패턴을 사용할 때는 부모 자식 클래스에 @SuperBuilder 어노테이션을 사용하면 된다.
setter 메서드를 지양하고 setter 메서드 대신 @Builder 어노테이션을 지향하자