티스토리 뷰
Item 2. 생성자에 매개변수가 많다면 빌더를 고려하라
Jordy-torvalds 2019. 12. 5. 21:54정적 팩토리 메서드와 생성자에 똑같은 제약이 하나 있다. 그것은 바로 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다.
만약에, 식품 포장의 영양정보를 표현하는 클래스를 생각해보자. 영양정보는 1회 내용량, 총 n회 제공량, 1회 제공량당 칼로리 같은 필수 항목 몇 개와 총지방, 탄수화물, 나트륨 등 수많은 선택 항목으로 이뤄진다. 이때 대다수 값이 0 이다.
그래서, 값이 있는 항목에 한해 객체 생성시 넣을 수 있도록 생성자를 생성하려하면 파라미터가 끝도 없이 길어질 수 있고, 또한 파라미터에 넣는 값의 순서가 바뀌어도 오류가 나지 않아 어플리케이션이 비정상 적으로 작동될 수 있다. 더 큰 문제는 비정상 작동은 큰 문제가 된 이후에 발견 될 것이다.
결론적으로 생성자의 매개 변수 조합이 다양하고 매개 변수 수 자체가 많을 수록 클라이언트(클래스를 호출해서 사용하는 클래스) 코드를 작성하거나 읽기 어려워 질 것이다.
이러한 점을 보완하고자 점층적 생성자 패턴, 자바빈즈 패턴이 나오긴 했지만 둘 모두 비효율적이였다.
두 패턴을 설명하기 위해 아래와 같은 클래스가 있다고 가정하겠다.
public class Niniz{
String name;
int age;
String job;
String address;
}
점층적 생성자 패턴
클래스 내 변수들을 하나씩 늘려가며 생성자를 만드는 패턴. 주요 변수가 많아지면 수평으로 무궁무진하게 길어질 수 있다.
public Niniz(String name){};
public Niniz(String name, int age){};
public Niniz(String name, int age, String job){};
public Niniz(String name, int age, String job, String address){};
자바 빈즈 패턴
객체 생성 후 주요 변수를 모두 set 해주는 패턴. 변수가 많은 수록 더 수직으로 더 길어 질 수 있다.
set이 끝나기 전까지는 객체가 불완전한 상태로 놓이게 되며, 그 과정에서 파라미터가 유효한지도 체크 할 수 없어 일관성 유지도 힘들다. 이러한 단점은 불변 클래스(객체 생성 후 변경 되지 않는 클래스)를 생성할 수 없게한다.
Niniz niniz= new Niniz();
niniz.setName("jordy");
niniz.setAge(2000);
niniz.setJob("Kakao Intern");
niniz.setAddress(null);
위 두 가지 패턴의 장점을 모두 취한 빌더 패턴이 있다.
- 클라이언트(빌더 패턴이 적용된 클래스를 사용하는 클래스)는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(혹은 정적 팩터리)를 호출해 빌더 객체를 얻는다.
- 그런 다음 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 매개변수들을 설정한다.
- 마지막으로 매개변수가 없는 build매서드를 호출해 드디어 우리에게 필요한 (보통은 불변인) 객체를 얻는다.
- 빌더는 생성할 클래스 안에 정적 멤버 클래스 (public static class) 로 만들어두는게 보통이다.
아래 코드를 보자.
// 영양성분 빌더 패턴 클래스
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 sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int setvings){
this.servingSize = servingSize;
this.servings = servingSize;
}
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){
carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
public NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
@Override
public String toString() {
return "NutritionFacts{" +
"servingSize=" + servingSize +
", servings=" + servings +
", calories=" + calories +
", fat=" + fat +
", sodium=" + sodium +
", carbohydrate=" + carbohydrate +
'}';
}
}
/* ------------------------------------------------------------------------------------*/
// Main 메소드
// 영양성분 클래스로 객체를 빌드 한 후 콘솔 확인.
public class Item2_Main {
public static void main(String[] args) {
NutritionFacts cocacola = new NutritionFacts.Builder(240,8)
.calories(100).sodium(35).sodium(27).build();
System.out.println(cocacola);
}
}
/* [console] */
NutritionFacts{servingSize=240, servings=240, calories=100, fat=0, sodium=27, carbohydrate=0}
Main 메소드의 클라이언트 코드는 쓰기 쉽고 읽기가 쉽다. 빌더 패턴은 (파이썬과 스칼라에 있는) 명명된 선택적 매개변수를 흉내낸 것이다.
위 코드에 추가적으로 유효성 검사 코드를 가미하면 완벽 하다고 한다. 유효성 검사 코드는 빌더의 생성자와 메서드에서 입력 매개변수를 검사하고, build 메서드가 호출하는 생성자에서 여러 매개변수에 걸친 불변식(invariant)를 검사하자. 공격에 대비해 불변식을 보장하려면 빌더로부터 매개변수를 복사한 후 해당 객체 필드들도 검사해야 한다.
불변 immutable
어떠한 변경도 허용하지 않는 다는 뜻
ex)String
불변식 invariant
프로그램이 실행되는 동안, 혹은 정해진 기간 동안 반드시 만족해야 하는 조건을 말한다.
빌더 패턴은 계층적으로 설계된 클래스와 함꼐 쓰기가 좋다.
'Essential Language Skill > Effective Java' 카테고리의 다른 글
Item 6. 불필요한 객체 생성을 피하라 (0) | 2019.12.08 |
---|---|
Item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2019.12.05 |
Item 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2019.12.05 |
Item 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2019.12.05 |
Item 1. 생성자 대신 정적 팩토리 메서드를 고려하라 (0) | 2019.12.05 |
- Total
- Today
- Yesterday
- Java
- Java Memory Structure
- Delete
- 자바 메모리 구조
- kubernetes
- Replication Controller
- JMM
- Effective Java
- POD
- k8s
- JVM
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |