빌더 패턴은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 맴개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드를 통해 객체를 생성하는 방식이다.
@Getter @Setter
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private int phoneNumber;
public Member(String name) {
this.name = name;
}
public Member(String name, int age) {
this.name = name;
this.age = age;
}
public Member(String name, int age, int phoneNumber) {
this.name = name;
this.age = age;
this.phoneNumber = phoneNumber;
}
}
Member memberA = new Member("memberA", 10, 112);
점증자 생성 패턴의 단점
문제를 해결하기 위해 "자바빈즈 패턴" 사용
@Getter @Setter
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private int phoneNumber;
public void setName(String name) {
this.name = namee;
}
public void setAge(int age) {
this.age = age;
}
public void setPhoneNumber(int phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
Member member = new Member();
member.setName("memberA");
member.setAge(20);
member.setPhoneNumber(112);
자바 빈즈 패턴 단점
둘의 단점을 모두 보완한 것이 빌더 패턴이다.
데이터는 자바 빈즈 패턴처럼 받고, 데이터 일관성을 위해 정보들을 모두 받은 후에 객체를 생성한다.
직접 빌더 클래스 생성
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private int phoneNumber;
private Member() {}
public static class Builder {
private String name;
private int age;
private int phoneNumber;
public Builder(String name){
this.name = name;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder phoneNumber(int phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
// 빌드 메서드
public Member build() {
Member member = new Member();
member.name = name;
member.age = age;
member.phoneNumber = phoneNumber;
return member;
}
}
}
Member member = new Member.Builder("memberA")
.age(25)
.phoneNumber(112);
.build(); //빌드 메서드로 새로운 객체 생성
@Builder
위와 같은 과정을 lombok의 @Builder를 사용하면 생략할 수 있다.
@Builder
@ToString
public class Member {
@Id @GeneratedValue(strategy = GnerationType.IDENTITY)
private Long id;
private String name;
private int age;
private int phoneNumber;
}
@Transactional
@Test
public void buildTest() {
Member memberA = Member.builder()
.name("memberA")
.age(20)
.phoneNumber(112)
.build();
Member memberB = Member.builder()
.name("memberB")
.age(25)
.phoneNumber(119)
.build();
memberRepository.save(memberA);
memberRepository.save(memberB);
List<Member> members = memberRepository.findAll();
for (member : members) {
log.info("member={}", member);
}
}
빌더 패턴을 사용해서 memberA, memberB를 생성하고 로그를 찍어보았다.

정상적으로 memberA, memberB 객체가 생성된 것을 확인할 수 있다.
❗ 참고
@Builder
@ToString
public class Member {
@Id @GeneratedValue(strategy = GnerationType.IDENTITY)
private Long id;
private String name;
private int age;
private int phoneNumber = 10;
}
@Test
@Transactional
public void save() {
Member memberA = Member.builder()
.name("memberA")
.age(20)
.build();
log.info("memberA={}", memberA);
}
그래서 phoneNumber를 10으로 설정해놓고 테스트를 돌려봤다.

phoneNumber=10으로 나올줄 알았는데 0으로 나왔다. 왜지?? 하고 찾아보니..
- 빌더 패턴은 빌드할 때 값을 설정해주지 않으면 null/0/false를 타입에 따라 반환한다.
- String 타입은 null, int 타입은 0, boolean 타입은 false를 반환한다.
그럼 기본 값 설정 방법이 없을까 ? @Builder.Default를 사용하면 된다.
@Builder.Default
@Builder
@ToString
public class Member {
@Id @GeneratedValue(strategy = GnerationType.IDENTITY)
private Long id;
private String name;
private int age;
@Builder.Default
private int phoneNumber = 10;
}
@Test
@Transactional
public void save() {
Member memberA = Member.builder()
.name("memberA")
.age(20)
.build();
log.info("memberA={}", memberA);
}
