개발

빌더 패턴과 @Builder: 객체 생성의 번거로움을 해결하자!

HipPopoTamUs 2024. 10. 9. 17:40

빌더 패턴이란?

빌더 패턴 은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴입니다.

여기서 객체의 표현 방법이라는 것은 생성하려는 객체의 필드를 어떤 순서로, 어떤 값으로 설정하는지. 예를 들어 이름만 설정할 수도 있고, 이름과 나이만 설정할 수도 있는 것처럼, 다양한 조합으로 객체를 만들 수 있는 방법을 말합니다.

 

생성자로 객체를 만들 때, 필요한 파라미터가 많아지면 매번 생성자 오버로딩을 통해 새로운 생성자를 만들어야 하는 번거로움이 있습니다. 그리고 필요한 생성자의 매개변수가 많아질 경우 문제가 발생합니다.

이렇게 되면 매개변수의 순서를 혼동하기 쉽고, 일부 매개변수에 대해 null 값 또는 기본값을 할당해야 할 때, 그 의미를 파악하기 어렵게 만들 수 있습니다. 이 문제를 빌더 패턴을 사용해 해결할 수 있습니다.

 

별도의 Builder 클래스를 만들어 필수 값에 대해서는 생성자를 사용하고,
선택적인 값들에 대해서는 메소드를 통해 필요한 값들을 입력받고 build() 메서드를 사용해 인스턴스를 리턴 받는 방식 입니다.

 

 

빌더 패턴을 통한 문제 해결

빌더 패턴은 객체 생성을 유연하고 가독성 있게 만들어줍니다. 필수적인 값만 설정하고, 선택적인 값들은 필요에 따라 설정할 수 있습니다.

public void 빌더로_문제를_해결해보자() { 
	// User 객체를 생성하는 UserBuilder 클래스를 사용 
	UserBuilder userBuilder = UserBuilder.builder(); // builder: 정적 메소드 
    
	userBuilder.name("matthew").birth("2001-07-09"); // 필요한 필드를 체이닝 방식으로 설정 
    
	User matthew = userBuilder.build(); // 최종적으로 설정된 객체 반환 
}

 

 

 

UserBuilder 클래스 구현

UserBuilder 클래스는 User 객체를 생성하는 데 사용됩니다. 각 필드를 설정하는 메소드를 제공하고, 메소드 체이닝을 통해 가독성 좋게 객체를 설정할 수 있습니다.

 
@NoArgsConstructor
public class UserBuilder {
	private User user = new User();  // User 객체 초기화
	
	// UserBuilder 인스턴스 반환하는 정적 메소드
	private static UserBuilder builder() {
		return new UserBuilder();
	}
	
	// 체이닝을 위한 name 필드 설정 메소드
	public UserBuilder name(String name) {
		user.setName(name);
		return this;  // 현재 UserBuilder 인스턴스 반환
	}
	
	// 체이닝을 위한 birth 필드 설정 메소드
	public UserBuilder birth(LocalDateTime time) {
		user.setBirth(time);
		return this;
	}
	
	// 설정된 User 객체 반환
	public User build() {
		return user;
	}
}

 

 

롬복(@Builder)으로 더 편리하게

롬복(Lombok) 라이브러리를 사용하면, 수동으로 빌더 패턴을 작성하지 않고도 자동으로 빌더 클래스를 생성할 수 있습니다. 이때, 빌더 클래스는 빌드하려는 클래스 내부에 static class로 생성됩니다.

@Builder
public class User {
	private String uuid;
	private String name;
	private LocalDateTime birth;
}



롬복을 사용한 빌더 패턴

롬복을 사용하면 더욱 간결하게 객체를 생성할 수 있습니다. 롬복은 자동으로 builder() 메소드와 build() 메소드를 만들어 주기 때문에 개발자는 필요한 필드만 설정하면 됩니다.

public void 롬복을_사용하면_편해요() {
	User user = User.builder()
			.name("matthew")
			.birth("2001-07-09")
			.build();
                
	// builder()와 build() 메소드는 롬복이 자동 생성
}

 

 

여기서 롬복이 자동으로 생성한 builder()와 build()는 각각 다음과 같은 역할을 합니다.

public class User {
    private String uuid;
    private String name;
    private LocalDate birth;
    
    User(String uuid, String name, LocalDate birth) {
    	this.uuid = uuid;
        this.name = name;
        this.birth = birth;
	}
	
    public static User.UserBuilder builder() {
    	return new User.UserBuilder();
    }

	public static class UserBuilder {  // 빌더 클래스
    
    	// 빌드하려는 클래스의 필드가 그대로 선언되어있음
    	private String uuid;
    	private String name;
    	private LocalDate birth;
        
        UserBuilder() {
        }
        
        // 일단 자신의 필드에 값 설정
        public User.UserBuilder uuid(String uuid) {
        	this.uuid = uuid;
            return this;
        }
        
        ... // name과 birth값을 설정하는 메소드 생략
        
        // 설정된 자신의 필드값을 사용해 그대로 User 객체 생성
        public User build() {
        	return new User(this.uuid, this.name, this.birth);
        }
        
        public String toString() {
        	return "User.UserBuilder(uuid=" + this.uuid + ", name=" + this.name + ", birth=" + this.birth + ")";
        }
    }
}

 

builder()

해당 클래스의 빌더 객체를 생성하는 정적 메소드

 

User.builder()를 호출하면 User 객체를 빌드하는 빌"더" 객체가 반환됩니다.

이 빌더 객체 내부에는 빌드하려는 클래스의 필드들이 동일하게 선언되어있고,

그 필드값을 설정하는 메소드도 만들어집니다. 이 메소드는 체이닝을 위해 자기 자신을 반환합니다.

 

build()

빌더 객체에서 필드값이 설정된 최종 객체를 반환하는 메소드

 

builder()로 생성한 빌더 객체에 필드값을 설정하고, build()를 호출하면,

설정된 값들을 가진 완성된 객체가 반환됩니다.

즉, build() 메소드는 빌더 객체에 저장된 필드값들을 읽어, 이 값을 사용하여 빌드하려는 클래스의 

새 인스턴스를 생성하고 반환합니다.

 

정리

그럼 builder()와 build() 메소드를 사용한 코드를 다시 볼까요?

User user = User.builder()
	.name("matthew")
        .birth("2000-01-01")
        .build();
}

 

1. User.builder() 호출로 UserBuilder 객체를 생성합니다.

2. 메소드 체이닝으로 UserBuilder 객체의 필드에 값을 설정합니다.

3. build() 호출로 UserBuilder 객체에 저장되어있던 필드값을 그대로 사용해 User 객체를 생성하고 반환합니다.

 


해당 글은 아래 영상을 참고하였습니다.

https://www.youtube.com/watch?v=rKpCtQL-C08&ab_channel=%EC%BD%94%EB%94%A9%ED%95%98%EB%8A%94%EC%98%A4%ED%9B%84