item 2 dodo4513 - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

μ•„μ΄ν…œ2 μƒμ„±μžμ— λ§€κ°œλ³€μˆ˜κ°€ λ§Žλ‹€λ©΄ λΉŒλ”λ₯Ό κ³ λ €ν•˜λΌ

  • 정적 νŒ©ν„°λ¦¬μ™€ μƒμ„±μžμ—λŠ” λ˜‘κ°™μ€ μ œμ•½μ΄ ν•˜λ‚˜ μžˆλ‹€. 선택적 λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ„ λ•Œ 적절히 λŒ€μ‘ν•˜κΈ° μ–΄λ ΅λ‹€λŠ” 점이닀.

λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ„ λ–„ μ‚¬μš©ν•˜λŠ” λŒ€μ•ˆ

점측적 μƒμ„±μž νŒ¨ν„΄: ν™•μž₯ν•˜κΈ° μ–΄λ ΅λ‹€.

  • 점측적 μƒμ„±μž νŒ¨ν„΄λ„ μ“Έ μˆ˜λŠ” μžˆμ§€λ§Œ, λ§€κ°œλ³€μˆ˜ κ°œμˆ˜κ°€ λ§Žμ•„μ§€λ©΄ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ±°λ‚˜ 읽기 μ–΄λ ΅λ‹€
// ν”νžˆ μ‚¬μš©λ˜λŠ” 점측적 μƒμ„±μž νŒ¨ν„΄ - ν™•μž₯ν•˜κΈ° 어렀움
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 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;
    }
}

μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄: 일관성이 깨지고, λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μ—†λ‹€.

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; }
}
  • 점측적 μƒμ„±μž νŒ¨ν„΄μ˜ 단점듀이 μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ—μ„œλŠ” 더 이상 보이지 μ•ŠλŠ”λ‹€. μ½”λ“œκ°€ 길어지긴 ν–ˆμ§€λ§Œ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κΈ° 쉽고, κ·Έ κ²°κ³Ό 더 읽기 μ‰¬μš΄ μ½”λ“œκ°€ λ˜μ—ˆλ‹€.
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
  • ν•˜μ§€λ§Œ λΆˆν–‰νžˆλ„ μžλ°”λΉˆμ¦ˆλŠ” μžμ‹ λ§Œμ˜ μ‹¬κ°ν•œ 단점을 μ§€λ‹ˆκ³  μžˆλ‹€. μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ—μ„œλŠ” 객체 ν•˜λ‚˜λ₯Ό λ§Œλ“€λ €λ©΄ λ©”μ„œλ“œλ₯Ό μ—¬λŸ¬ 개 ν˜ΈμΆœν•΄μ•Ό ν•˜κ³ , 객체가 μ™„μ „νžˆ μƒμ„±λ˜κΈ° μ „κΉŒμ§€λŠ” 일관성(consistency)이 λ¬΄λ„ˆμ§„ μƒνƒœμ— λ†“μ΄κ²Œ λœλ‹€.
  • 이처럼 일관성이 λ¬΄λ„ˆμ§€λŠ” 문제 λ•Œλ¬Έμ— μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ—μ„œλŠ” 클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μ—†λ‹€.

λΉŒλ” νŒ¨ν„΄ : 점측적 μƒμ„±μž νŒ¨ν„΄κ³Ό μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ˜ μž₯점만 μ·¨ν–ˆλ‹€.

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 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 sodium(int val) {
            sodium = val;
            return this;
        }

        public Builder carbohydrate(int val) {
            carbohydrate = 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;
    }
}
  • λΉŒλ” νŒ¨ν„΄μ€ (파이썬과 μŠ€μΉΌλΌμ— μžˆλŠ”) λͺ…λͺ…λœ 선택적 λ§€κ°œλ³€μˆ˜(named optional parameters)λ₯Ό 흉내 λ‚Έ 것이닀.

핡심정리

μƒμ„±μžλ‚˜ 정적 νŒ©ν„°λ¦¬κ°€ μ²˜λ¦¬ν•΄μ•Ό ν•  λ§€κ°œλ³€μˆ˜κ°€ λ§Žλ‹€λ©΄ λΉŒλ” νŒ¨ν„΄μ„ μ„ νƒν•˜λŠ” 게 더 λ‚«λ‹€. λ§€κ°œλ³€μˆ˜ 쀑 λ‹€μˆ˜κ°€ ν•„μˆ˜κ°€ μ•„λ‹ˆκ±°λ‚˜ 같은 νƒ€μž…μ΄λ©΄ 특히 더 κ·Έλ ‡λ‹€. λΉŒλ”λŠ” 점측적 μƒμ„±μžλ³΄λ‹€ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό 읽고 μ“°κΈ°κ°€ 훨씬 κ°„κ²°ν•˜κ³ , μžλ°”λΉˆμ¦ˆλ³΄λ‹€ 훨씬 μ•ˆμ „ν•˜λ‹€.

끝

λΉŒλ” νŒ¨ν„΄μ„ μœ„ν•œ 좔가적인 μ½”λ“œκ°€ λ§Žμ•„λ³΄μ΄λ‚˜, Lobmok의 @Builder의 도움을 λ°›μœΌλ©΄ ν•œκ²° μˆ˜μ›”ν•˜κ²Œ κ΅¬ν˜„ν•  수 μžˆλ‹€.