본문 바로가기

Design Patterns

Builder Pattern

class에 선택적 인자가 다수 있는 경우 생성자를 어떻게 구현하는 것이 적합한가.

 

여러 방법이 존재하겠지만 처음 생각해볼 수 있는 방법은 점층적 생성자 패턴일 것이다.

필수인자를 받는 생성자를 정의하고, 선택적 인자를 점층적으로 추가하는 방식으로 생성자들을 구현하는 것이다. 하지만 점층적 생성자 패턴은 인자 수가 늘어나면 클라이언트 코드를 작성하기 어려워지고, 코드가 읽기 어려워진다는 문제가 있다. 또한 인자들이 서로 바뀌어 들어가게 되었을 때 발생하는 버그를 쉽게 잡아내기 어렵게 되는 등 문제가 발생하기 쉽다.

 

그래서 이에 대한 대안으로 자바빈 패턴을 이용할 수 있다. 

자바빈 패턴은 인자없는 생성자를 호출하여 객체를 생성하고, setter method를 이용하여 필드 값들을 채워나가는 방식을 의미한다. 자바빈 패턴은 점층적 생성자 패턴의 단점을 해결해 줄 수 있지만, 1회 함수 호출로 객체 생성을 끝낼 수 없고, 객체 일관성이 일시적으로 깨질 수 있다.또한 자바빈 패턴은 애초에 생성된 객체에 인자 값을 추가하는 방식으로 구현되기 때문에, 변경 불가능한 클래스를 만들 수 없다는 한계가 있다.

 

점층적 생성자 패턴의 안정성과 자바빈 패턴의 가독성을 결합한 세 번째 대안이 빌더 패턴이다.

클라이언트는 필수 인자들은 생성자에 전달하여 빌더 객체를 생성하고, 빌더 객체에 정의된 성정 메서드를 호출하여 선택적 인자를 추가해나간다. 그리고 마지막으로 build 메서드를 호출하여 변경 불가능한 객체를 만드는 것이다.

 

public class BuilderPatternTest {
	public static void main(String[] args) {
		final User user = User.builder("code0xff", "code0xff@xxxxx.com", "01000000000")
				.age(35).marriage(true).build();
	}
}

class User {
	private final String userId;
	private final String email;
	private final String mobile;
	private final Integer age;
	private final Boolean marriage;
	
	private User(UserBuilder builder) {
		userId = builder.userId;
		email = builder.email;
		mobile = builder.mobile;
		age = builder.age;
		marriage = builder.marriage;
	}

	public static UserBuilder builder(String userId, String email, String mobile) {
		return new UserBuilder(userId, email, mobile);
	}

	static class UserBuilder {
		private final String userId;
		private final String email;
		private final String mobile;
		private Integer age;
		private Boolean marriage;
		
		public UserBuilder(String userId, String email, String mobile) {
			this.userId = userId;
			this.email = email;
			this.mobile = mobile;
		}
		
		public UserBuilder age(Integer age) {
			this.age = age;
			return this;
		}
		
		public UserBuilder marriage(Boolean marriage) {
			this.marriage = marriage;
			return this;
		}
		
		public User build() {
			return new User(this);
		}
	}
}

 

위 코드를 분석하면 User라는 객체는 생성시에 userId, email, mobile 값을 필수적으로 받고, age와 marriage는 선택적으로 입력받는 형식을 갖고 있다는 것을 알 수 있다. 이경우 UserBuilder를 생성할 때 필수값을 받게 하고 이후 age와 marriage 함수를 선택적으로 호출하여 값을 입력받을 수 있다. 마지막으로 build 함수를 호출하여 불변성이 깨지지 않는 User 객체를 생성할 수 있는 것이다. 

 

이처럼 빌더 패턴을 이용하여 인자에 불변식을 적용할 수 있으며, 빌더 객체로 여러 객체를 생성할 수 있다는 장점을 가지고 있다. 하지만 객체 생성을 하기 위해서는 빌더 객체를 먼저 생성해야하기 때문에 성능적인 면에서 문제가 될 수 있으며, 점층적 생성자 패턴에 비하여 코드량이 많다는 단점 때문에 인자 수가 충분히 많은 경우 사용하는 것이 좋다.

 

lombok을 적용한 경우에는 @Builder 애너테이션을 사용하여 바로 Builder 패턴을 사용할 수 있기 때문에 코드량이 많아진다는 단점을 극복할 수 있다.

 

@Builder
public class User {
	private String userId;
	private String email;
	private String mobile;
	private Integer age;
	private Boolean marriage;
}

'Design Patterns' 카테고리의 다른 글

Observer Pattern  (0) 2020.07.05