선택적 매개변수가 많을 때 개발자들이 사용하는 방법 3
1. 점층적 생성자 패턴
생성자 형태를 모든 경우의 수로 만드는 방법
단점 :
- 매개변수가 많아질수록 클라이어느 코드를 작성하거나 읽기 어렵다
- 매개변수 세기도 어렵고...
- 버그가 발생하면 찾기가 어렵다
public class Burger {
private final boolean olive;
private final boolean pickled;
private final int patty;
private final int egg;
private final int cheese;
private final int tomato;
private final int bacon;
public Burger (boolean olive, boolean pickled){
this(olive, pickled, 1);
//이때 1은 코드를 효율적으로 짜기위해 없는 매개변수 부분을 1로 default 시키는 방법
}
public Burger (boolean olive, boolean pickled, int patty){
this(olive, pickled, patty, 1);
//this는 생성자 자기 자신을 의미
}
...
public Burger (boolean olive, boolean pickled, int patty, int egg, int cheese, int tomato, int bacon){
this.olive = olive;
this.pickled = pickled;
this.patty = patty;
this.egg = egg;
this.cheese = cheese;
this.tomato = tomato;
this.bacon = bacon;
//이때 this는 위에서 선언한 필드를 의미
}
}
2. 자바빈즈
Burger burger = new Burger();
burger.setOlive(true);
burger.setPickled(true);
burger.setPatty(1);
burger.setEgg(1);
burger.setCheese(1);
burger.setTomato(1);
burger.setBacon(1);
단점 :
- 객체 하나를 완성하기 위해서는 메서드를 여러번 호출해야하고, 따라서 완성되기 전에 일관성이 무너지게 된다.
- 클래스를 불변으로 만들 수 없다
- 스레드 안정성을 위한 추가적 작업이 필요
- '얼리고'를 사용하지만 실전에서 거의 사용 x
- 컴파일러가 보증할 방법이 없어서 런타임 오류에 취약
3. 빌더 패턴
클래스는 불변이 되고, 모든 매개변수가 한 곳에 모여짐. 빌더의 세터 메서드들은 빌더 자신을 반환하여 연쇄적으로 호출가능(플루언트 API or 메서드 연쇄)
public class BurgerIngredients {
private final boolean olive;
private final boolean pickled;
private final int patty;
private final int egg;
private final int cheese;
private final int tomato;
private final int bacon;
public static class Builder {
//필수로 올리브 피클 유무
private final boolean olive;
private final boolean pickled;
// 선택 - 기본 한장 초기화(필수값만 final이고 선택은 final x)
private int patty = 1;
private int egg = 1;
private int cheese = 1;
private int tomato = 1;
private int bacon = 1;
public Builder(boolean olive, boolean pickled){
this.olive = olive;
this.pickled = pickled;
}
public Builder patty(int count)
{ patty = count; return this; }
public Builder egg(int count)
{ egg = count; return this; }
public Builder cheese(int count)
{ cheese = count; return this; }
public Builder tomato(int count)
{ tomato = count; return this; }
public Builder bacon(int count)
{ bacon = count; return this; }
public BurgerIngredients build(){
return new BurgerIngredients(this);
}
}
//Builder 안에 한꺼번에!
//BurgerIngredients의 필드들은 final 이기 때문에 클래스는 불변이 된다
private BurgerIngredients(Builder builder){
olive = builder.olive;
pickled = builder.pickled;
patty = builder.patty;
egg = builder.egg;
cheese = builder.cheese;
tomato = builder.tomato;
bacon = builder.bacon;
}
}
//호출
//연쇄적으로 호출되는 모습
BurgerIngredients cheeseburger = new BurgerIngredients.Builder(true, true).cheese(2).build();
▶ 추가 : 계층적으로 설계된 클래스에 빌더 패턴 사용
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
public abstract class Hamburger {
public enum Ingredient { PATTY, EGG, CHEESE, TOMATO, BACON}
final Set<Ingredient> ingredients;
abstract static class Builder <T extends Builder <T>> {
EnumSet<Ingredient> ingredients = EnumSet.noneOf(Ingredient.class); //EnumSet 정리
public T addIngredient(Ingredient ingredient){
ingredients.add(Objects.requireNonNull(ingredient)); //static method 정리
return self();
}
abstract Hamburger build();
protected abstract T self(); //셀프타입 관용구 - 추상메서드
}
Hamburger(Builder<?> builder){ //어떤 햄버거를 만들지 모르니 위에서부터 다 제네릭
ingredients = builder.ingredients.clone(); //NutritionFacts 클래스 불변 참조
}
}
※ EnumSet
EnumSet 은 Enum을 담는 변수로 List<String>은 String을 담는 변수라면 EnumSet은 Enum을 담는 그릇 EnumSet.noneOf 는 EnumSet을 빈그릇으로 만드는 것.
대신 이 그릇엔 Ingredient 라는 Enum의 값들만 들어올 수 있음
※static method
매개변수는 있지만, 바로 return 되어 반한되어짐
이런 속성을 이용한 것이 static method
여기서는 Objects라는 객체를 통해 requireNonNull이라는 메서드를 사용하고 매개변수가
null인지 체크해서 null이면 NullPointException 이 발생하도록 하는 것
※ .clone()
생성자에서는 필드에 각각 값을 final로 생성해서 만들었다면 여기서는 .clone을 통해 불변화시키는 모습
import java.util.Objects;
public class CheeseBurger extends Hamburger {
public enum CheeseSize { Single, Duuble, King}
private final CheeseSize cheeseSize;
public static class Builder extends Hamburger.Builder<Builder> {
private final CheeseSize cheeseSize;
public Builder(CheeseSize cheeseSize){
this.cheeseSize = Objects.requireNonNull(cheeseSize);
}
//@Override를 통해 상속되었음을 표시
@Override public CheeseBurger build() {
return new CheeseBurger(this);
}
@Override protected Builder self() {return this;}
}
private CheeseBurger(Builder builder){
super(builder);
cheeseSize = builder.cheeseSize;
}
}
'자바' 카테고리의 다른 글
(이펙티브 자바) 아이템 06. 불필요한 객체 생성을 피하라 (0) | 2020.07.27 |
---|---|
(이펙티브 자바) 아이템 08. finalizer와 cleaner 사용을 피하라 (0) | 2020.07.26 |
(이펙티브 자바) 아이템 03. private 생성자나 열거타입으로 싱글턴임을 보증하라 (0) | 2020.07.20 |
(이펙티브 자바) 아이템 04. 인스턴스화를 막으려거든 private 생성자를 사용하라 (1) | 2020.07.19 |
(이펙티브 자바) 아이템 01. 생성자 대신 정적 팩터리 메서드를 고려하라. (1) | 2020.07.19 |