πŸ“‚ JAVA/μ΄νŽ™ν‹°λΈŒ μžλ°”

μƒμ„±μžμ—κ²Œ λ§€κ°œλ³€μˆ˜κ°€ λ§Žλ‹€λ©΄ λΉŒλ”λ₯Ό κ³ λ €ν•˜λΌ - [2μž₯ 객체 생성과 파괴(μ•„μ΄ν…œ2)]

Amenable 2022. 11. 26. 18:29

  μ•„λž˜ μ½”λ“œμ²˜λŸΌ μƒμ„±μžμ™€ μƒμ„±μžμ˜ νŒŒλΌλ―Έν„°κ°€ λ§Žμ€ κ²½μš°μ—λŠ” μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ?

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 fianl int carbohydrate; // (g/1회 μ œκ³΅λŸ‰) - 선택
    
    public NutritionFacts(int servingSize, int servings){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = 0;
        this.fat = 0;
        this.sodium = 0;
        this.carbohydrate = 0;
    }
    
    public NutritionFacts(int servingSize, int servings, int calories){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = 0;
        this.sodium = 0;
        this.carbohydrate = 0;
    }
    
    public NutritionFacts(int servingSize, int servings, int calories, int fat){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = 0;
        this.carbohydrate = 0;
    }
    
    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = 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;
    }
}

  이런 경우 ν•΄λ‹Ή λ‹¨μ›μ—μ„œλŠ” λΉŒλ”μ˜ μ‚¬μš©μ„ 고렀해보라고 ν•˜κ³  μžˆλ‹€. λΉŒλ”λ₯Ό μ‚¬μš©ν•˜κΈ°μ— μ•žμ„œ ν˜„μž¬ 일반적으둜 μ‚¬μš©ν•˜κ³  μžˆλŠ” 방법듀을 μ•Œμ•„λ³΄κ³  μ™œ λΉŒλ”λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λŠ”μ§€ μƒκ°ν•΄λ³΄μž. μ„€λͺ… μˆœμ„œλŠ” 1) 점측적 μƒμ„±μž νŒ¨ν„΄(telescoping constructor pattern), 2) μžλ°”λΉˆμ¦ˆ(JavaBeans pattern), 3) λΉŒλ” νŒ¨ν„΄(Builder pattern)으둜 μ§„ν–‰λœλ‹€.

 

1. 점측적 μƒμ„±μž νŒ¨ν„΄(telescoping constructor pattern)

  이 방식은 ν•„μˆ˜ 맀개 λ³€μˆ˜λ§Œ λ°›λŠ” μƒμ„±μž, ν•„μˆ˜ λ§€κ°œλ³€μˆ˜μ™€ 선택 λ§€κ°œλ³€μˆ˜ 1개λ₯Ό λ°›λŠ” μƒμ„±μž, 선택 λ§€κ°œλ³€μˆ˜λ₯Ό 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 fianl 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, servings, calories, fat, sodium, carbohydrate);
    }
}

  이 방법은 λ¬Έμ œμ μ€ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€ λ•Œ μ–΄λ–€ νŒŒλΌλ―Έν„°λ₯Ό μ€˜μ•Ό ν•˜λŠ”μ§€ μ•ŒκΈ° μ–΄λ ΅λ‹€λŠ” 것이닀. 예λ₯Ό λ“€μ–΄, 첫 번째 νŒŒλΌλ―Έν„°, 두 번째 νŒŒλΌλ―Έν„°, μ„Έ 번째 νŒŒλΌλ―Έν„°,..., n 번째 νŒŒλΌλ―Έν„°μ— 각각 무엇을 μ€˜μ•Ό ν•˜λŠ”μ§€ μ•ŒκΈ° μ–΄λ ΅λ‹€λŠ” 것이닀.

 

2. μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄(JavaBeans pattern)

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 fianl int carbohydrate; // (g/1회 μ œκ³΅λŸ‰) - 선택
    
    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;
    }
}

  이 방법은 μž₯점은 μ•„λž˜μ™€ 같이 객체 생성이 간단해진닀. ν•˜μ§€λ§Œ ν•„μˆ˜ κ°’λ“€(servingSize, servings)이 섀정이 λ˜μ§€ μ•Šμ€ μƒνƒœλ‘œ κ·Έλƒ₯ μ‚¬μš©λ  수 μžˆλ‹€λŠ” 단점이 μžˆλ‹€. 즉, λΆˆμ•ˆμ •ν•œ μƒνƒœ(일관성(consistency)이 λ¬΄λ„ˆμ§„ μƒνƒœ)둜 μ‚¬μš©λ  μ—¬μ§€κ°€ μžˆλ‹€λŠ” λ¬Έμ œκ°€ μžˆλ‹€. 

public static void main(String[] args){
    NutritionFacts cocaCola = new NutritionFacts();
    cocaCola.setServingSize(240);
    // cocaCola.setServings(100); // ν•„μˆ˜ κ°’(servings)을 μ„€μ •ν•˜μ§€ μ•Šμ€ μƒνƒœ
    
    cocaCola.setCalories(100);
}

  λ˜ν•œ, μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ μ–΄λ””κΉŒμ§€κ°€ ν•„μˆ˜ 값인지 μ•Œ μˆ˜κ°€ μ—†λ‹€. μœ„μ˜ μ½”λ“œμ²˜λŸΌ servingSizeκΉŒμ§€ 섀정을 ν•˜λ©΄ ν•„μˆ˜ 값을 λͺ¨λ‘ μ„€μ •ν•œ 것인지, μ•„λ‹ˆλ©΄ servingSize와 servingsλ₯Ό λͺ¨λ‘ μ„€μ •ν•΄μ•Ό ν•„μˆ˜ 값듀을 λͺ¨λ‘ μ„€μ •ν•œ 것인지 μ•Œ μˆ˜κ°€ μ—†λ‹€. 

 

3. λΉŒλ” νŒ¨ν„΄(Builder pattern)

  λ§ˆμ§€λ§‰ λŒ€μ•ˆμ΄ λ°”λ‘œ λΉŒλ” νŒ¨ν„΄μ΄λ‹€. μ΄λŠ” 점측적 μƒμ„±μž νŒ¨ν„΄μ˜ μ•ˆμ „μ„±κ³Ό μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄μ˜ 가독성을 κ²ΈλΉ„ν•œ 것이닀.

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;
    }
}
public static main(String[] args){
    NutritionFacts cocaCola = new Builder(240, 8).caloreis(100).sodium(35).build();
}

 

  객체λ₯Ό μƒμ„±ν•˜λŠ” 방법은 λ‹€μŒκ³Ό κ°™λ‹€.

  1. ν•„μˆ˜ 맀개 λ³€μˆ˜λ§ŒμœΌλ‘œ μƒμ„±μž(ν˜Ήμ€ 정적 νŒ©ν„°λ¦¬)λ₯Ό ν˜ΈμΆœν•΄ λΉŒλ” 객체λ₯Ό μ–»λŠ”λ‹€.
  2. λΉŒλ” 객체가 μ œκ³΅ν•˜λŠ” μΌμ’…μ˜ μ„Έν„° λ©”μ„œλ“œ(μ„Έν„°λŠ” μ•„λ‹˜)λ“€λ‘œ μ›ν•˜λŠ” 선택 λ§€κ°œλ³€μˆ˜λ“€μ„ μ„€μ •ν•œλ‹€.
  3. λ§€κ°œλ³€μˆ˜κ°€ μ—†λŠ” build λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄ μš°λ¦¬μ—κ²Œ ν•„μš”ν•œ 객체λ₯Ό μ–»λŠ”λ‹€.

  μΌμ’…μ˜ μ„Έν„° λ©”μ„œλ“œλ“€μ„ μ‚¬μš©ν•¨μœΌλ‘œμ¨ ν”Œλ£¨μ–ΈνŠΈ API(fluent API) ν˜Ήμ€ λ©”μ„œλ“œ 연쇄(method chaining)λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

 

 

  μ΄μƒμœΌλ‘œ μƒμ„±μžμ˜ λ§€κ°œλ³€μˆ˜κ°€ λ„ˆλ¬΄ λ§Žμ€ κ²½μš°μ— μ‚¬μš©ν•  수 μžˆλŠ” 1) 점측적 μƒμ„±μž νŒ¨ν„΄(telescoping constructor pattern), 2) μžλ°”λΉˆμ¦ˆ(JavaBeans pattern), 3) λΉŒλ” νŒ¨ν„΄(Builder pattern)을 μ•Œμ•„λ³΄μ•˜λ‹€. μ•žμ˜ λ¬Έμ œμ λ“€μ„ ν•΄κ²°ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ§„ν–‰λ˜μ—ˆμ§€λ§Œ, κ·Έλ ‡λ‹€κ³  λͺ¨λ“  κ²½μš°μ— λΉŒλ”κ°€ μ μ ˆν•˜λ‹€λŠ” 것은 μ•„λ‹ˆλ‹€.  λΉŒλ”λŠ” μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€κ³  ν•„λ“œκ°€ μ€‘λ³΅λœλ‹€λŠ” λ¬Έμ œκ°€ μžˆλ‹€. (λ¬Όλ‘  Lombok의 @Builderλ₯Ό μ‚¬μš©ν•˜λŠ” 방법이 있긴 ν•˜μ§€λ§Œ 이것 λ˜ν•œ 뢀가적인 문제λ₯Ό κ°€μ§€κ³  μžˆλ‹€.)

  λ”°λΌμ„œ 'ν•„μˆ˜μ μΈ ν•„λ“œμ™€ ν•„μˆ˜μ μ΄μ§€ μ•Šμ€ ν•„λ“œκ°€ 있고 이것 λ•Œλ¬Έμ— μƒμ„±μžκ°€ λ„ˆλ¬΄ 많이 λŠ˜μ–΄λ‚˜λŠ” 상황'κ³Ό '클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€κ³  싢은 상황'μ—μ„œ λΉŒλ” νŒ¨ν„΄μ˜ μ‚¬μš©μ„ ν•œλ²ˆ κ³ λ €ν•΄λ³΄μž!

 

ν•΄λ‹Ή 글은 λ°±κΈ°μ„  λ‹˜μ˜ 'μ΄νŽ™ν‹°λΈŒ μžλ°” μ™„λ²½ 곡랡'을 μˆ˜κ°•ν•˜κ³  μž‘μ„±ν•œ κ²ƒμž…λ‹ˆλ‹€.