1HOON
논리적 코딩
1HOON
전체 방문자
오늘
어제
  • HOME (187)
    • ☕️ JAVA (28)
      • WhiteShip Java LIVE Study (6)
      • Effective JAVA (10)
    • 🔮 KOTLIN (4)
    • 🌱 SPRING (51)
      • 스프링 인 액션 (22)
      • JPA (18)
    • ☕️ JAVASCRIPT (6)
    • 📃 DATABASE (40)
      • ORACLE (37)
      • MSSQL (2)
    • 🐧 LINUX (4)
    • 🐳 DOCKER (5)
    • 🐙 KUBERNETES (4)
    • 🏗️ ARCHITECTURE (8)
    • 📦 ETC (27)
      • TOY PROJECT (5)
      • RECRUIT (1)
      • 그냥 쓰고 싶어서요 (14)
      • TIL (1)
    • 🤿 DEEP DIVE (1)
    • 🚽 Deprecated (9)
      • PYTHON (3)
      • AWS (2)
      • HTTP 완벽가이드 (3)
      • WEB (1)

블로그 메뉴

  • 홈
  • 방명록
  • 관리

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
1HOON

논리적 코딩

[이펙티브자바] 규칙02 : 생성자 인자가 많을 때는 Builder 패턴을 고려하라
☕️ JAVA

[이펙티브자바] 규칙02 : 생성자 인자가 많을 때는 Builder 패턴을 고려하라

2017. 10. 7. 17:54


인스턴스를 생성할 때, 여러 인자의 값을 초기화 해줘야 할 때가 있다. 이번 규칙에서는 세 가지 패턴을 비교해 효율적인 방법을 제시한다.


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


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
32
33
34
public class NutritionFacts {
 
    private final int SERVING_SIZE;
    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.SERVING_SIZE = servingSize;
        this.SERVINGS = servings;
        this.CALORIES = calories;
        this.FAT = fat;
        this.SODIUM = sodium;
        this.CARBOHYDRATE = carbohydrate;
    }
}
Colored by Color Scripter
cs


위 클래스를 인스턴스화 하기 위해서는 아래와 같이 인자 개수에 맞는 생성자를 호출하면 된다. 


1
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 3, 35, 27);
cs


하지만, 이런 방식에는 문제가 있다. 인자의 수가 많거나, 설정할 필요가 없는 인자도 전달해야하는 상황이 생긴다. 그리고 생성자를 호출한 코드에서 매개변수가 어떤 인자를 초기화하는지 알기 힘들다. 예를들어, 지금 매개변수 100이 어떤 인자를 초기화할까? 한 번에 대답하기 힘들 것이다.


책에서 요약한 내용은 다음과 같다.

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


 자바 빈 패턴(java bean pattern)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
 
    public NutritionFacts(){ }
 
    public void setServingSize(int val){ servingSize = val; }
    public void setServings(int val){ servings = val; }
    public void setCalories(int val){ calories = val; }
    public void setFat(int val){ fat = val; }
    public void setSodium(int val){ sodium = val; }
    public void setCarbohydrate(int val){ carbohydrate = val; }
    
}
Colored by Color Scripter
cs


위 클래스를 인스턴스화 하기 위해선 생성자를 호출하면된다. 하지만 실제로 쓸모있게? 사용하려면 setter를 이용해 값을 초기화 시켜줘야한다.


1
2
3
4
5
6
7
NutritionFacts cocaCola = new NutritionFacts();
 
cocaCola.setServingSize(240);
cocaCola.setSevings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
cs


코드의 길이는 조~금 길지만 가독성은 좋다. 객체 생성하기도 쉽고! 하.지.만 이 방식도 문제가 있다.

바로 변경 불가능한 클래스를 만들 수 없다는 것이다! 만약 위 클래스에 private final int CONSTANT; 라는 인자가 있을 때, 초기화를 시켜줄 수 있을까? 


책에서 지적한 내용은 다음과 같다.

1회의 함수 호출로 객체 생성을 끝낼 수 없으므로, 객체 일관성이 일시적으로 깨질 수 있다.

자바빈 패턴으로는 변경 불가능 클래스를 만들 수 없다는 것이다.


 빌더 패턴(builder pattern)


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package rule02;
 
public class NutritionFact {
    
    private final int SERVING_SIZE;
    private final int SERVINGS;
    private final int CALORIES;
    private final int FAT;
    private final int SODIUM;
    private final int CARBOHYDRATE;
    private int variable;
    
    public int getVariable() {
        return variable;
    }
    
    public void setVariable(int variable) {
        this.variable = variable;
    }    
 
    public static class Builder {
        //필수인자
        private final int SERVING_SIZE;
        private final int SERVINGS;
        //선택적인자
        private int CALORIES = 0;
        private int FAT = 0;
        private int SODIUM = 0;
        private int CARBOHYDRATE = 0;
        
        public Builder(int servingSize, int servings) {
            this.SERVING_SIZE = 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 NutritionFact build() {
            return new NutritionFact(this);
        }
    }
    
    private NutritionFact(Builder builder) {
        SERVING_SIZE = builder.SERVING_SIZE;
        SERVINGS = builder.SERVINGS;
        CALORIES = builder.CALORIES;
        FAT = builder.FAT;
        SODIUM = builder.SODIUM;
        CARBOHYDRATE = builder.CARBOHYDRATE;
    }
    
}
 
Colored by Color Scripter
cs


위 클래스는 필요한 객체를 직접 만들지 않고, 필수 인자들을 생성자에 전달해 빌더 객체(Builder)를 만든다. 그리고 빌더 객체의 메서드를 호출해 선택적 인자들을 추가한다. 마지막으로 build()메서드를 호출해 변경 불가능한 클래스를 만든다.


실제로 위 클래스를 객체로 만드는 코드는 다음과 같다.


1
2
NutritionFact cocaCola = new NutritionFact.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();        
cocaCola.setVariable(12);
Colored by Color Scripter
cs


정말, 간결하고, 직관적인 코드가 아닐 수가 없다. 나는 이 코드를 보고 충격을 먹었다.

그리고 앞으로 적극적으로 빌더 패턴을 사용할 것을 다짐했다.


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

반응형
저작자표시 비영리 변경금지 (새창열림)

'☕️ JAVA' 카테고리의 다른 글

Spring AOP와 AspectJ 비교하기  (14) 2019.12.15
이미지를 BLOB 형태로 DB에 저장하기  (5) 2018.09.09
웹 서버와 웹 어플리케이션 서버의 차이  (1) 2017.12.22
예외(Exception) - 잘못된 처리들  (0) 2017.12.04
[이펙티브자바] 규칙05 : 불필요한 객체는 만들지 말라  (0) 2017.10.10
    '☕️ JAVA' 카테고리의 다른 글
    • 이미지를 BLOB 형태로 DB에 저장하기
    • 웹 서버와 웹 어플리케이션 서버의 차이
    • 예외(Exception) - 잘못된 처리들
    • [이펙티브자바] 규칙05 : 불필요한 객체는 만들지 말라
    1HOON
    1HOON

    티스토리툴바