-
iterm 2. 생성자에 매개변수가 많다면 빌더를 고려하라.정적 팩토리와 생성자에는 공통적으로 제약이 있다. 책에 나온 예시를 보고 설명해보겠다. 식품 포장의 영양정보를 표현하는 클래스가 있다고 생각해보자.
등등 …. 식품의 영양정보가 총 20개가 있다고 가정해보자. 대부분의 제품은 선택 항목 중 대다수의 값이 그냥 0이다. 이렇게 많은 필드를 가지고 있는 것들은 생성자와 정적 팩터리는 어떤 모습을 하고 있을까?
public class NutritionFacts {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택 선택
private final int fat; // (g/1회 제공량) 선택
private final int sodium; // (mg/1회 제공량) 선택
private final int carbohydrate; // (g/1회 제공량) 선택
public NutritionFacts2(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = 0;
this.fat = 0;
this.sodium = 0;
this.carbohydrate = 0;
}
public NutritionFacts2(int servingSize, int servings, int calories) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = 0;
this.sodium = 0;
this.carbohydrate = 0;
}
public NutritionFacts2(int servingSize, int servings, int calories, int fat) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = 0;
this.carbohydrate = 0;
}
public NutritionFacts2(int servingSize, int servings, int calories, int fat, int sodium) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = 0;
}
public NutritionFacts2(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class NutritionFacts {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택 선택
private final int fat; // (g/1회 제공량) 선택
private final int sodium; // (mg/1회 제공량) 선택
private final int carbohydrate; // (g/1회 제공량) 선택
public NutritionFacts2(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts2(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts2(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts2(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
} 점층적 생성자 패턴점층적 생성자 패턴이란?
점층적 생성자 패턴의 단점인스턴스를 만들 때 어떤 파라미터를 줘야하는지 모른다.. 아래와 같이 첫번째는 servingSize, servings .. 하지만 기억하기 어렵다. NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); ➡️단점을 극복 할려고 만든 기능 1 💡 Tip. 인텔리제이에서는 파라미터의 정보들을 알려준다. -> Mac OS : Command + p
➡️단점을 극복하려고 만든 기능 2 하지만.. 인텔리제이가 없는 곳이라면..? 🤔 → 일일이 클래스로 들어가서 체크하면서 파라미터를 넣어줬다고한다. 위와 같이 파라미터가 많은 경우
하지만 또 다른 방법이 있다. 바로.. 자바 빈즈(JavaBeans pattern)자바 빈즈란?
public class NutritionFacts3 {
//필드 (기본값이 있다면) 기본값으로 초기화한다.
private int servingSize = -1; // 필수; 기본값 없음
private int servings = -1 ; // 필수; 기본값 없음
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts3() { // 기본 생성자
}
public void setServingSize(int servingSize) {
this.servingSize = servingSize;
}
public void setServings(int servings) {
this.servings = servings;
}
public void setCalories(int calories) {
this.calories = calories;
}
public void setFat(int fat) {
this.fat = fat;
}
public void setSodium(int sodium) {
this.sodium = sodium;
}
public void setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
} 자바빈즈의 장점아래와 같이 인스턴스를 만드는 것이 간단해진다. NutritionFacts cocaCola = new NutritionFacts(); 자바빈즈의 단점
불변 객체로 만들려면 ?
빌더(Builder)
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{
//1. 필수 매개변수
private final int servingSize;
private final int servings;
//①선택 매개변수 - 기본값으로 초기화한다.
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
//2. 필수 매개변수는 생성자로 넣어준다.
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
//3. 설정을해야하는 것들 -> setter와 같은 기능이라고 생각하면된다.
public Builder calories(int val){
calories = val;
return this;
}
public Builder fat(int val){
fat = val;
return this;
}
public Builder sodium(int val){
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
this.carbohydrate = val;
return this;
}
// 4.빌드 메서드 -> 실제 객체를 만들어준다.
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;
}
} 위와같이 코드를 작성하면.. 메소드 체이닝이 가능하다.
public static void main(String[] args) {
NutritionFacts cocaCola = new Builder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();
} 빌더 패턴의 장점
빌더 패턴의 단점
빌더는 이럴 때 쓰자.필수적인 필드와 선택적인 필드가 섞여 있을경우 매개변수의 생성자가 많고 불변(immutable)하게 만들고 싶을 때 고려해보자! 번외빌더 패턴의 단점 극복
import lombok.Builder;
@Builder
public class NutritionFacts4 {
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 void main(String[] args) {
NutritionFacts4 nutritionFacts4 = new NutritionFacts4Builder()
.servingSize(100)
.servings(101)
.build();
}
} 코드가 확 줄어드는 것(간결)을 알 수 있다. 간략한 롬복 원리룸복이 컴파일 시점에서 코드를 조작한다.
컴파일 코드를 확인해보면
하지만 단점이 존재한다. @builder만 쓸 경우의 단점
모든 파라미터를 받는 생성자를 만들기 싫다면..?
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static void main(String[] args) {
NutritionFacts cocaCola = new Builder(240, 8) // X 안됨
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();
} 필수 매개변수를 생성자로 넣고 선택 매개변수를 넣는 것이 어려워진다. 계층적으로 설계된 클래스에서 빌더패턴 |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
오,, 롬복까지 같이 정리해주셨네요 👍 @Builder
public class ProductsStockApiRequest {
@Builder.Default
List<ProductStockApiRequest> products = List.of();
} |
Beta Was this translation helpful? Give feedback.
오,, 롬복까지 같이 정리해주셨네요 👍
말씀해주신대로 롬복의 단점이 필수 값을 지정할 수는 없어서,, 그 부분을 조금 유의해야할 거 같습니다.
추가로 Builder 애노테이션을 쓰면 @Builder.Default로 디폴트값을 지정해줄 수 있습니다.
만약의 필수값인데 빌더 생성 시 누락 했을때 기본값이 세팅되도록,, 그렇게 단점을 살짝 극복할 수 있을 거 같긴 하네요.
https://projectlombok.org/features/Builder