Builder 패턴이란?

wnajsldkf·2023년 3월 2일
0

Java

목록 보기
18/19
post-thumbnail

객체를 생성하기 위한 방법 중 하나이다.

왜 @Builder 패턴을 사용할까?

Effective Java 아이템2를 살펴보면, "생성자에 매개변수가 많다면 빌더를 고려하라."를 확인할 수 있다.

정적 팩터리와 생성자 방식은 매개변수가 다양하면 대응이 어렵다는 단점이 있다.
이를 해결하기 위해 1) 점층적 생성자 패턴을 사용할 수 있다.

public class User {
	private final String name;		// 필수
    private final int age;			// 선택1
    private final String gender;	// 선택2
    
	// 필수 생성자만 필요한 경우
    public User(String name) {
    	this.name = name;
        this.age = "나이";
        this.gender = "성별";
	}
    
    // 선택1만 필요한 경우
    public User(String name, int age) {
    	this.name = name;
        this.age = age;
        this.gender = "성별";
	}
    
    // 모든 인자가 필요한 경우
    public User(String name, int age, String gender) {
    	this.name = name;
        this.age = age;
        this.gender = gender;
	}
}    

점층적 패턴은 인자가 추가되면 코드를 작성하기도, 읽기도 어려워진다. 또 호출되는 코드만 봐서 어떤 인자인지 알기 어렵다는 단점이 있다. (ex. User user = new User("jeongin", 15);)

다음은 2) 자바빈 패턴을 사용한다. 이 방법은 setter를 사용해서 생성 코드를 작성하는 방법이다. 여러 개의 생성자를 만들지 않아도 된다. 하지만 객체 생성 시 여러번 호출하여 일관성이 깨지고, 변경 불가능한 클래스를 만들 수 없다.

3) 빌더 패턴
기계인간님의 코드를 가져왔습니다.

// Effective Java의 Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters(필수 인자)
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values(선택적 인자는 기본값으로 초기화)
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;    // 이렇게 하면 . 으로 체인을 이어갈 수 있다.
        }
        public Builder fat(int val) {
            fat = val;
            return this;
        }
        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }
        public Builder sodium(int val) {
            sodium = val;
            return this;
        }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

위의 코드를 작성하면 Builder 패턴으로 객체를 생성할 수 있다.

NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8);
builder.calories(100);
builder.sodium(35);
builder.carbohydrate(27);
NutritionFacts cocaCola = builder.build();
// 각 줄마다 builder를 타이핑하지 않아도 되어 편리하다.
NutritionFacts cocaCola = new NutritionFacts
    .Builder(240, 8)    // 필수값 입력
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();           // build() 가 객체를 생성해 돌려준다.

위와 같은 이유로 Builder 패턴은 다음 장점을 갖는다.
1. 필요한 데이터만 설정 가능
2. 유연성 확보 가능
3. 높은 가독성
4. 변경 가능성 최소화

@Builder

Builder 패턴을 직접 만든다면 위 코드처럼 빌더를 만들고, 멤버 필드별로 값을 설정하고 빌더를 반환하는 함수를 만들어야 한다.
@Builder 애노테이션을 사용하면 이것을 간단하게 사용할 수 있다.

@Builder
public class User {
	private final String name;		
    private final int age;			
    private final String gender;	
}    

생성자에 @Builder를 붙여준 경우

public class User {
	private final String name;		
    private final int age;			
    private final String gender;	

	@Builder 
    public User(String name, int age, String gender) {
    	this.name = name; 
        this.age = age;
        this.gender = gender;
	}        
}    

Builder 패턴을 사용하여 객체 생성

User.UserBuilder builder = User.builder();
builder.name("name");
builder.age(20);
User user = builder.build();

체인을 사용한 경우

User user = User.builder()
	.name("name");
    .age(20)
    .build();

클래스 선언부에 @Builder는 사용하지 않는다. 클래스 선언부의 @Builder@AllArgsConstructor를 달아주는 것과 같으므로 생성자에 사용하는 것이 바람직하다.

Builder 패턴에 대해 알아보았다. Builder 패턴을 사용하려면 Builder를 생성해야 한다는 비용이 있다. 하지만 우리가 개발하는 API는 확장되는 경향이 많으므로 Builder 패턴을 사용하는 것이 필요하다.

Reference

profile
https://mywnajsldkf.tistory.com -> 이사 중

0개의 댓글