빌더(Builder) 패턴
빌더 패턴은 복잡한 객체 생성을 단계별로 처리하며, 객체 생성 과정과 객체의 표현 방법을 분리하는 패턴입니다. 이 패턴은 다양한 속성을 가진 객체를 생성할 때, 매개변수의 수가 많아 코드가 복잡해지거나 가독성이 떨어지는 문제를 해결합니다.
문제점: 복잡한 객체 생성과 가독성 저하
아래 예제는 생성자(Constructor)를 통해 객체를 생성하는 방식입니다. 만약 객체가 많은 속성을 가진다면, 생성자에 매개변수가 너무 많아져 코드의 가독성이 떨어지고 유지보수가 어려워집니다.
class User {
private String name;
private String email;
private String address;
private String phoneNumber;
private String jobTitle;
public User(String name, String email, String address, String phoneNumber, String jobTitle) {
this.name = name;
this.email = email;
this.address = address;
this.phoneNumber = phoneNumber;
this.jobTitle = jobTitle;
}
@Override
public String toString() {
return "User{name='" + name + "', email='" + email + "', address='" + address + "', phoneNumber='" + phoneNumber + "', jobTitle='" + jobTitle + "'}";
}
}
생성자에 너무 많은 매개변수를 전달해야 하며, 매개변수 순서를 잘못 전달할 가능성이 높고, 선택적인 매개변수에 대해 모든 조합을 지원하려면 생성자 오버로딩이 필요합니다.
해결 방법: 빌더 패턴 적용
Builder 패턴은 객체 생성 로직을 분리하여 단계별로 명확하게 속성을 설정하도록 합니다. 이 패턴을 사용하면 매개변수 순서에 상관없이 객체를 생성할 수 있고, 필수/선택 매개변수를 유연하게 설정할 수 있습니다.
class User {
private String name;
private String email;
private String address;
private String phoneNumber;
private String jobTitle;
// private 생성자
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.address = builder.address;
this.phoneNumber = builder.phoneNumber;
this.jobTitle = builder.jobTitle;
}
@Override
public String toString() {
return "User{name='" + name + "', email='" + email + "', address='" + address + "', phoneNumber='" + phoneNumber + "', jobTitle='" + jobTitle + "'}";
}
// Builder 클래스 (내부 정적 클래스)
public static class UserBuilder {
private String name;
private String email;
private String address;
private String phoneNumber;
private String jobTitle;
// 필수 매개변수 설정
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
// 선택 매개변수 설정 메서드
public UserBuilder setAddress(String address) {
this.address = address;
return this;
}
public UserBuilder setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public UserBuilder setJobTitle(String jobTitle) {
this.jobTitle = jobTitle;
return this;
}
// User 객체 생성 메서드
public User build() {
return new User(this);
}
}
}
이때의 클라이언트 코드는 다음과 같습니다.
public class Main {
public static void main(String[] args) {
User user = new User.UserBuilder("으악이", "euak@example.com")
.setAddress("seoul")
.setPhoneNumber("010-1234-5678")
.setJobTitle("백수")
.build();
System.out.println(user);
// 출력: User{name='으악이', email='euak@example.com', address='seoul', phoneNumber='010-1234-5678', jobTitle='백수'}
}
}
빌더 패턴의 장단점
빌더 패턴은 메서드 체이닝 방식으로 객체의 속성을 설정하므로 코드가 명확하고 직관적입니다. 그리고 선택적인 매개변수에 대해 생성자 오버로딩 없이 필요한 속성만 설정할 수 있습니다. 또한, 객체가 생성된 후에는 수정되지 않도록 설계할 수 있고, 선택적인 매개변수를 자유롭게 설정할 수 있으며, 필요한 매개변수만 설정하도록 유도합니다.
그러나 객체를 생성하기 위해 빌더 클래스가 필요하므로 코드가 길어지고 클래스가 늘어날 수 있습니다. 또한, 간단한 객체 생성에는 오히려 불필요한 복잡성을 초래할 수 있습니다.
결론
빌더 패턴은 매개변수가 많거나 복잡한 객체를 유연하고 직관적으로 생성할 수 있게 도와주는 강력한 패턴입니다. 객체 생성 과정을 명확하게 표현하며, 불변 객체를 쉽게 만들 수 있는 장점이 있습니다. 하지만, 작은 객체나 간단한 객체 생성에는 오히려 복잡성이 증가할 수 있으므로 상황에 따라 적절히 사용하는 것이 중요합니다.