티스토리 뷰

Java

Builder패턴

SonSeungWoo 2018. 3. 6. 21:55

생성자 인자가 많을 때는 Builder패턴 적용을 고려하자


생성자와 Static 팩토리 메소드는 같은 문제를 가지고 있다. 선택적 인자가 많은 상황에 잘 적응하지 못한다는 것이다.

이런 상황에 어떤 생성자나 정적 팩터리 메서드가 적합할지 고민을 한다.


1.점층적 생성자 패턴(telescoping constructor)

필수 인자만 받는 생성자를 하나 정의하고, 선택적 인자를 하나 받는생성자를 추가하고, 거기에 두 개의  선택적 인자를 받는 생성자를 추가하는 방식이다.

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 NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}

public NutritionFacts(int servingSize, int servings, int calories){
this(servingSize, servings, calories, 0);
}

public NutritionFacts(int servingSize, int servings, int calories, int fat){
this(servingSize, servings, calories, fat, 0);
}

public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium){
this(servingSize, servings, calories, fat, sodium, 0);
}

public NutritionFacts(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,5,103,2,35,75);

점층적 생성자 패턴은 잘 동작하지만 인자 수가 늘어나면 클라이언트 코드를 작성하기가 어려워지고, 무엇보다 읽기 어려운 코드가 된다.



2.자바빈즈(javaBeans) 패턴 - 일관성 훼손이 가능하고, 항상 변경 가능하다.

인자 없는 생성자를 호출하여 객체부터 만든 다음, 설정 메서드(setter methods)들을 호출하여 필수 필드뿐 아니라 선택적 필드의 값들 까지 채우는 것이다.

public class NutritionFacts {
private int servingSize = -1; //필수: 기본값 없음
private int servings = -1; //필수
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;

private NutritionFacts() {}

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();
cocaCola.setServingSize(1);
cocaCola.setServings(2);
cocaCola.setCalories(3);
cocaCola.setFat(4);
cocaCola.setSodium(5);
cocaCola.setCarbohydrate(6);

그러나 1회의 함수 호출로 객체 생성을 끝낼 수 없으므로, 객체 일관성이 일시적으로 깨질 수 있다. 자바빈 패턴으로는 변경불가능 클래스를

만들 수 없다는 것이다. 스레드 안전성을 제공하기 위해 해야 할 일도 많아진다.



3.빌더(builder) 패턴

점층적 생성자 패턴의 안전성에 자바빈 패턴의 가독성을 결합한 패턴이다. 필요한 객체를 직접 생성하는 대신, 클라이언트는 먼저 필수 인자들을 생성자에(또는 정적 팩터리 메소드) 전부 전달하여 빌더객체(builder object)를 만든다. 그런 다음 빌더 객체에 정의된 설정 메서드들을 호출하여 선택적 인자들을 추가해 나간다. 그리고 마지막으로 아무런 인자 없이 build 메서드를 호출하여 변경불가능(immutable) 객체를 만드는 것이다. 빌더 클래스는 빌더가 만드는 객체 클래스의 정적 멤버 클래스로 정의한다.

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 {
// 필수
private final int servingSize;
private final int servings;
// 선택
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;
}
}

객체가 변경 불가능하다는 사실, 그리고 모든 인자의 기본값이 한곳에 모여 있다는 것에 유의하기 바란다. 그리고 빌더의 setter 메소드들은 연속적으로 여러 번 호출될 수 있도록 빌더 자신의 객체를 반환한다.

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).
sodium(35).carbohydrate(27).build();

빌더 패턴은 인자가 많은 생성자나 정적 팩터리가 필요한 클래스를 설계할 때, 특히 대부분의 인자가 선택적 인자인 상황에 유용하다.

'Java' 카테고리의 다른 글

static과 final의 사용  (0) 2017.06.28
자바7의 몇 가지 특징  (0) 2017.03.30
JAVA8에 대해 알아보자(2)  (0) 2017.03.29
JAVA8에 대해 알아보자(1)  (0) 2017.03.29
jvm 메모리  (0) 2017.03.29
공지사항
최근에 올라온 글
최근에 달린 댓글
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함