Home [Effective Java] Item 2
Post
Cancel

[Effective Java] Item 2

Effective Java - Item 2

정적 νŒ©ν† λ¦¬μ™€ μƒμ„±μžλŠ” 선택적 λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ„ λ•Œμ—λŠ” λŒ€μ‘ν•˜κΈ° μ–΄λ ΅λ‹€λŠ” λ™μΌν•œ μ œμ•½μ΄ μ‘΄μž¬ν•œλ‹€. 이런 문제의 ν•΄κ²°μ±…μœΌλ‘œ μ„Έ 가지 νŒ¨ν„΄μ„ λ“€ 수 μžˆλŠ”λ°, λ°”λ‘œ 점측적 μƒμ„±μž νŒ¨ν„΄, μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄ 그리고 λΉŒλ” νŒ¨ν„΄μ΄λ‹€.

❓ 점측적 μƒμ„±μž νŒ¨ν„΄ (Telescoping Constructor Pattern)μ΄λž€?

ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λ§Œ λ°›λŠ” μƒμ„±μž, ν•„μˆ˜ λ§€κ°œλ³€μˆ˜μ™€ 선택 λ§€κ°œλ³€μˆ˜ 1개λ₯Ό λ°›λŠ” μƒμ„±μž, 선택 λ§€κ°œλ³€μˆ˜λ₯Ό 2개 λ°›λŠ” μƒμ„±μž ν˜•νƒœλ‘œ 선택 λ§€κ°œλ³€μˆ˜λ₯Ό μ „λΆ€ λ‹€ λ°›λŠ” μƒμ„±μžκΉŒμ§€ λŠ˜λ €κ°€λŠ” 방식이닀.

  • μ½”λ“œ

    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
    
      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 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;
      	}
      }
    

    β†’ ν•΄λ‹Ή ν΄λž˜μŠ€μ—μ„œ ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λŠ” 2κ°œλΏμ΄μ§€λ§Œ, μ΄μ™Έμ˜ 선택 λ§€κ°œλ³€μˆ˜λ“€λ„ μ–΄μ©” 수 없이 값을 지정 ν•΄μ€˜μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 효율이 떨어진닀.

ν•˜μ§€λ§Œ λ§€κ°œλ³€μˆ˜ κ°œμˆ˜κ°€ λ§Žμ•„μ§€λ©΄ λ§Žμ•„μ§ˆμˆ˜λ‘ λ§Žμ€ 쑰합이 λ§Œλ“€μ–΄μ§€κ³ , μƒμ„±μžμ˜ μˆ˜κ°€ λ§Žμ•„μ Έ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ μž‘μ„± 효율과 가독성이 떨어진닀.

λ˜ν•œ 인자둜 λ°›λŠ” λ§€κ°œλ³€μˆ˜ νƒ€μž…μ΄ 같은 κ²½μš°μ—λŠ” μƒμ„±μžλ₯Ό λ§Œλ“€ 수 없을 λΏλ”λŸ¬ μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜λŠ” μž…μž₯μ—μ„œλŠ” ν•΄λ‹Ή λ§€κ°œλ³€μˆ˜μ™€ κ°œμˆ˜κ°€ λ§žλŠ”μ§€ 항상 확인 ν•΄μ•Όν•œλ‹€λŠ” λΆˆνŽΈν•¨μ΄ λ”°λ₯Έλ‹€.

❓ μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄ (JavaBeans Pattern)μ΄λž€?

λ§€κ°œλ³€μˆ˜κ°€ μ—†λŠ” μƒμ„±μžλ‘œ 객체λ₯Ό λ§Œλ“  ν›„, μ„Έν„°(setter) λ©”μ„œλ“œλ“€μ„ ν˜ΈμΆœν•΄ μ›ν•˜λŠ” λ§€κ°œλ³€μˆ˜μ˜ 값을 μ„€μ •ν•˜λŠ” 방식이닀. μ½”λ“œλŠ” κΈΈμ–΄μ§€μ§€λ§Œ 점측적 μƒμ„±μž νŒ¨ν„΄μ— λΉ„ν•΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κΈ° 쉽고 가독성 λ˜ν•œ μ’‹λ‹€.

  • μ½”λ“œ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      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; }
      }
    

    β†’ ν•΄λ‹Ή ν΄λž˜μŠ€μ—μ„œ ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λŠ” 2κ°œλΏμ΄μ§€λ§Œ, μ΄μ™Έμ˜ 선택 λ§€κ°œλ³€μˆ˜λ“€λ„ μ–΄μ©” 수 없이 값을 지정 ν•΄μ€˜μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 효율이 떨어진닀.

κ·ΈλŸ¬λ‚˜ 객체 ν•˜λ‚˜λ₯Ό μƒμ„±ν•˜λ €λ©΄ λ©”μ„œλ“œλ₯Ό μ—¬λŸ¬ 개 ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€λŠ” 단점도 μ‘΄μž¬ν•œλ‹€. λ˜ν•œ ν•œ 번의 μƒμ„±μž 호좜만으둜 생성이 μ™„λ£Œλ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 일관성(consistency)이 λ¬΄λ„ˆμ§€κ³  λ³€ν•˜μ§€ μ•ŠλŠ” λΆˆλ³€(immutable) 객체λ₯Ό λ§Œλ“€ 수 μ—†λ‹€.

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

❓ λΉŒλ” νŒ¨ν„΄ (Builder Pattern)μ΄λž€?

ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λ§ŒμœΌλ‘œ 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ λΉŒλ” 객체λ₯Ό 얻은 λ’€, λΉŒλ” 객체가 μ œκ³΅ν•˜λŠ” μ„Έν„° λ©”μ„œλ“œλ“€λ‘œ ν•„μš”ν•œ 선택 λ§€κ°œλ³€μˆ˜λ₯Ό μž…λ ₯ν•  수 μžˆλ‹€.

각각의 μ„Έν„° λ©”μ„œλ“œλŠ” 값을 μ„€μ •ν•œ λ’€ 자기 μžμ‹ (Builder)을 λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— μ—°μ†ν•΄μ„œ λ©”μ„œλ“œ 호좜이 κ°€λŠ₯ν•˜κ³ , λ§ˆμ§€λ§‰μœΌλ‘œ build() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ ν•„μš”ν•œ 객체λ₯Ό μ™„μ„±ν•˜μ—¬ λ°˜ν™˜ν•  수 μžˆλ‹€.

  • μ½”λ“œ

    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
    
      package effectiveJava.item02;
        
      public class NutritionFacts3 {
      	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 NutritionFacts3 build() {
      			return new NutritionFacts3(this);
      		}
      	}
        	
      	private NutritionFacts3(Builder builder) {
      		servingSize = builder.servingSize;
      		servings = builder.servings;
      		calories = builder.calories;
      		fat = builder.fat;
      		sodium = builder.sodium;
      		carbohydrate = builder.carbohydrate;
      	}
      }
    

    β†’ 점측적 μƒμ„±μž νŒ¨ν„΄μ— λΉ„ν•΄ ν•„μš”ν•œ μΈμŠ€ν„΄μŠ€λ“€λ§Œ μƒμ„±ν•˜κΈ° 쉽닀.

    πŸ‘‰ κ³„μΈ΅μ μœΌλ‘œ μ„€κ³„λœ ν΄λž˜μŠ€μ— λΉŒλ” νŒ¨ν„΄ μ‚¬μš© 예제

    • μ½”λ“œ

      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
      
        package effectiveJava.item02;
      
        import java.util.Objects;
        import java.util.EnumSet;
        import java.util.Set;
      
        public abstract class Pizza {
            public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}
            final Set<Topping> toppings;
      
            abstract static class Builder<T extends Builder<T>> {
                EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
      
                public T addTopping(Topping topping) {
                    toppings.add(Objects.requireNonNull(topping));
                    return self();
                }
      
                abstract Pizza build();
      
                // ν•˜μœ„ ν΄λž˜μŠ€λŠ” 이 λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ "this"λ₯Ό λ°˜ν™˜ν•΄μ•Όν•œλ‹€.
                protected abstract T self();
            }
      
            Pizza(Builder<?> builder) {
                toppings = builder.toppings.clone();
            }
        }
      

      β†’ 좔상 λ©”μ„œλ“œ self() λŠ” ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ ν˜•λ³€ν™˜μ„ ν•˜μ§€ μ•Šκ³ λ„ 자기 μžμ‹ μ„ λ¦¬ν„΄ν•˜μ—¬ λ©”μ„œλ“œλ₯Ό μ—°μ‡„μ μœΌλ‘œ ν˜ΈμΆœν•  수 μžˆλ„λ‘ ν•œλ‹€.

      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
      
        package effectiveJava.item02;
      
        public class NyPizza extends Pizza {
          public enum Size {SMALL, MEDIUM, LARGE}
            private final Size size;
      
            public static class Builder extends Pizza.Builder<Builder> {
                private final Size size;
      
                public Builder(Size size) {
                    this.size = size;
                }
      
                @Override
                NyPizza build() {
                    return new NyPizza(this);
                }
      
                @Override
                protected Builder self() {
                    return this;
                }
            }
      
            private NyPizza(Builder builder) {
                super(builder);
                size = builder.size;
            }
        }
      

      β†’ Pizza 의 ν•˜μœ„ 클래슀, 크기(size)λ₯Ό ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€.

      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
      
        package effectiveJava.item02;
      
        public class Calzone extends Pizza { 
            private final boolean sauceInside;
      
            public static class Builder extends Pizza.Builder<Builder> {
              private boolean sauceInside = false;
      
                public Builder sauceInside() {
                    this.sauceInside = true;
                    return this;
                }
      
                @Override
                Calzone build() {
                    return new Calzone(this);
                }
      
                @Override
                protected Builder self() {
                    return this;
                }
            }
      
            private Calzone(Builder builder) {
                super(builder);
                sauceInside = builder.sauceInside;
            }
        }
      

      β†’ Pizza 의 ν•˜μœ„ 클래슀, μ†ŒμŠ€ 선택(sauceInside)λ₯Ό ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€.

      1
      2
      3
      4
      
        NyPizza pizza = new NyPizza.Builder(SMALL)
                .addTopping(ONION).build();
        Calzone calzone = new Calzone.Builder().sauceInside()
                .addTopping(HAM).build();
      

      β†’ NyPizza와 Calzone 객체 생성

    μœ„μ˜ μ½”λ“œμ—μ„œ NyPizza.BuilderλŠ” NyPizzaλ₯Ό λ°˜ν™˜ν•˜κ³  Calzone.BuilderλŠ” Calzoneλ₯Ό λ°˜ν™˜ν•œλ‹€. 이처럼 ν•˜μœ„ 클래슀의 λ©”μ†Œλ“œκ°€ μƒμœ„ 클래슀의 λ©”μ†Œλ“œμ—μ„œ μ •μ˜ν•œ λ°˜ν™˜ νƒ€μž…μ΄ μ•„λ‹Œ, κ·Έ ν•˜μœ„ νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ” κΈ°λŠ₯을 곡변 λ°˜ν™˜ 타이핑(convariant return typing)이라고 ν•œλ‹€.<JDK 1.5 이상> ν•΄λ‹Ή κΈ°λŠ₯ 덕뢄에 ν΄λΌμ΄μ–ΈνŠΈλŠ” ν˜•λ³€ν™˜μ— μ‹ κ²½ 쓰지 μ•Šκ³  λΉŒλ”λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

λ¬Όλ‘  λΉŒλ” νŒ¨ν„΄λ„ 단점이 μ—†λŠ” 것은 μ•„λ‹ˆλ‹€. 객체λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ¨Όμ € λΉŒλ” ν΄λž˜μŠ€λΆ€ν„° μ •μ˜ν•΄μ•Ό ν•œλ‹€. λΉŒλ”μ˜ 생성 λΉ„μš©μ΄ 큰 것은 μ•„λ‹ˆμ§€λ§Œ, μ„±λŠ₯에 λ―Όκ°ν•œ μƒν™©μ—μ„œλŠ” λ¬Έμ œκ°€ 될 수 μžˆλ‹€.

λ˜ν•œ λ§€κ°œλ³€μˆ˜κ°€ λͺ‡ 개 μ—†κ³ , 변경될 κ°€λŠ₯성이 μ•„μ˜ˆ μ—†λ‹€λ©΄ μ½”λ“œκ°€ λ‹€μ†Œ μž₯ν™©ν•œ λΉŒλ“œ νŒ¨ν„΄λ³΄λ‹€λŠ” 점측적 μƒμ„±μž νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λŠ” 것이 더 μœ λ¦¬ν•˜λ‹€.

그리고 μΆ”κ°€λ‘œ λΉŒλ” νŒ¨ν„΄μ„ μž‘μ„±ν•˜λŠ” 쉽고 νŽΈλ¦¬ν•œ 방법이 μ‘΄μž¬ν•˜λŠ”λ° λ°”λ‘œ Lombok 라이브러리의 @Builder μ–΄λ…Έν…Œμ΄μ…˜μ΄λ‹€. @Builder μ–΄λ…Έν…Œμ΄μ…˜μ€ λͺ¨λ“  멀버 ν•„λ“œμ— λŒ€ν•΄μ„œ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ” κΈ°λ³Έ μƒμ„±μžλ₯Ό λ§Œλ“€μ–΄μ£ΌκΈ° λ•Œλ¬Έμ— λ”°λ‘œ Builder Classλ₯Ό μž‘μ„±ν•  ν•„μš”κ°€ μ—†μ–΄ μ½”λ“œ μž‘μ„±μ— 맀우 μš©μ΄ν•˜λ‹€.

λ‹€λ§Œ μ ‘κ·Ό 레벨이 default이기 λ•Œλ¬Έμ—, 동일 νŒ¨ν‚€μ§€ λ‚΄μ—μ„œ ν•΄λ‹Ή μƒμ„±μžλ₯Ό 호좜 ν•  수 μžˆλŠ” λ¬Έμ œκ°€ 생긴닀. κ·ΈλŸ¬λ―€λ‘œ 객체 생성 μ‹œ λ°›μ•„μ•Ό ν•˜λŠ” λ§€κ°œλ³€μˆ˜λ“€λ§Œ μ‘΄μž¬ν•˜λŠ” μƒμ„±μžλ₯Ό λ§Œλ“€κ³  κ·Έ μƒμ„±μžμ— @Builder λ₯Ό μ§€μ •ν•˜λŠ” 것이 λ°”λžŒμ§ν•˜λ‹€.

This post is licensed under CC BY 4.0 by the author.

[Effective Java] Item 1

[Effective Java] Item 3