Lombok으로 @Builder 어노테이션을 사용하여 간단하게 사용하던 것을 실제로 구현을 하면서 알게된 것들을 작성하게 되었습니다.
⭐️ 참고 사이트 : https://refactoring.guru/design-patterns/builder
빌더란 복잡한 객체를 단계별로 생성할 수 있는 '생성 디자인 패턴' 입니다.
이 패턴을 사용하면 동일한 구성 코드를 사용해서 객체의 다양한 유형과 표현을 할 수 있습니다.
집을 예시로 들면, 집에는 창고가 있을 수도 있고, 수영장이나 정원이 있을 수도 있습니다.
이를 코드로 표현하면 아래와 같이 만들 수 있습니다.
class House {
widows, doors, rooms,
hasGarage, hasSwimPool, hasGarden, ...
}
하지만 각기 다른 집을 생성하기 위해 아래와 같이 매우 보기 좋지 않은 코드를 사용해야 합니다.
// House - 1
new House(4, 2, 4, true, null, null, null)
// House - 2
new House(4, 2, 4, true, true, true, true)
이를 위한 해결방법으로 Builder Pattern이 있습니다.
Builder Pattern은 자체 클래스에서 객체 생성 코드를 추출하여, builders 라는 별도의 객체로 이동하도록 제안합니다.
Builder 패턴을 사용하게 되면,
사용하는 멤버변수에만 데이터를 설정할 수 있고,
클래스에 멤버변수가 추가 되더라도 생성자를 쓰는 경우에는 모두 수정을 해줘야 하지만 빌더패턴을 쓰게 되면 기존 코드를 수정할 필요가 없는 유연함을 가지고 있고,
코드의 가독성을 높일 수 있습니다.
아래는 User Class 를 Builder Pattern 을 사용한 예제 입니다.
...
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import static com.google.common.base.Preconditions.checkArgument;
public class User {
private final Long seq;
private final Email email;
private Password passwd;
private int loginCount;
private LocalDateTime lastLoginAt;
private final LocalDateTime createAt;
public User(Builder builder) {
this.seq = builder.seq;
this.email = builder.email;
this.passwd = builder.passwd;
this.loginCount = builder.loginCount;
this.lastLoginAt = builder.lastLoginAt;
this.createAt = builder.createAt;
}
//== 비지니스 로직 ==//
public void updateLoginInfo() {
this.loginCount += 1;
this.lastLoginAt = LocalDateTime.now();
}
public Long getSeq() {
return seq;
}
public Email getEmail() {
return email;
}
public Password getPasswd() {
return passwd;
}
public int getLoginCount() {
return loginCount;
}
public LocalDateTime getLastLoginAt() {
return lastLoginAt;
}
public LocalDateTime getCreateAt() {
return createAt;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("seq", seq)
.append("email", email)
.append("email", email)
.append("email", email)
.append("email", email)
.append("email", email)
.append("email", email)
.toString();
}
public static class Builder {
private Long seq;
private Email email;
private Password passwd;
private int loginCount = 0;
private LocalDateTime lastLoginAt;
private LocalDateTime createAt = LocalDateTime.now();
public Builder seq(Long seq) {
this.seq = seq;
return this;
}
public Builder email(Email email) {
this.email = email;
return this;
}
public Builder passwd(Password passwd) {
this.passwd = passwd;
return this;
}
public Builder loginCount(int loginCount) {
this.loginCount = loginCount;
return this;
}
public Builder lastLoginAt(LocalDateTime lastLoginAt) {
this.lastLoginAt = lastLoginAt;
return this;
}
public Builder createAt(LocalDateTime createAt) {
this.createAt = createAt;
return this;
}
public User build() {
checkArgument(email != null, "email must be provided.");
checkArgument(passwd != null, "password must be provided.");
return new User(this);
}
}
}