Skip to content

Latest commit

ย 

History

History
454 lines (313 loc) ยท 17.6 KB

File metadata and controls

454 lines (313 loc) ยท 17.6 KB

์ƒ์„ฑ์ž์— ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์œผ๋ฉด ๋นŒ๋”๋ฅผ ๊ณ ๋ คํ•˜๋ผ

์ฑ… ์ •๋ฆฌ

์ฐธ์กฐ์•„์ดํ…œ

์•„์ดํ…œ51: ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์‹ ์ค‘ํžˆ ์„ค๊ณ„ํ•˜๋ผ. (๋ฉ”์„œ๋“œ, ์ด๋ฆ„ ๋ณ€์ˆ˜๋ช…)
์•„์ดํ…œ17: ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๋ผ. (๋ถˆ๋ณ€ ํด๋ž˜์Šค)
์•„์ดํ…œ50: ์ ์‹œ์— ๋ฐฉ์–ด์  ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด๋ผ.
์•„์ดํ…œ30: ์ด์™•์ด๋ฉด ์ œ๋„ค๋ฆญ ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“ค์–ด๋ผ.

์ •์  ํŒฉํ† ๋ฆฌ์™€ ์ƒ์„ฑ์ž์—๋Š” ๋˜‘๊ฐ™์€ ์ œ์•ฝ์ด ํ•˜๋‚˜์žˆ๋‹ค. ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์„ ๋•Œ ์ ์ ˆํžˆ ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ์ ์ด๋‹ค. ํด๋ž˜์Šค์šฉ ์ƒ์„ฑ์ž ํ˜น์€ ์ •์ ํŒฉํ† ๋ฆฌ๋Š” ์„ ๋ฐฐ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์€ ์ด๋Ÿด ๋•Œ ์ƒ์„ฑ์ž ํŒจํ„ด(telescoping constructor pattern)์„ ์ฆ๊ฒจ ์‚ฌ์šฉํ–ˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ•„์ˆ˜์ , 1๊ฐœ, 2๊ฐœ ๋“ฑ๋“ฑ ํ˜•ํƒœ๋กœ ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ „๋ถ€ ๋‹ค ๋ฐ›๋Š” ์ƒ์„ฑ์ž๊นŒ์ง€ ๋Š˜๋ ค๊ฐ€๋Š” ๋ฐฉ์‹์ด๋‹ค.

01. ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด

์ด ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ์›ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•œ ์ƒ์„ฑ์ž ์ค‘ ๊ฐ€์žฅ ์งง์€ ๊ฒƒ์„ ๊ณจ๋ผ ํ˜ธ์ถœ

์ •ํ™•ํžˆ๋Š” "ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž 1๊ฐœ, ๊ทธ๋ฆฌ๊ณ  ์„ ํƒ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ•˜๋‚˜์”ฉ ๋Š˜์—ฌ๊ฐ€๋ฉฐ ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“œ๋Š” ํŒจํ„ด"

๋ฌธ์ œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์›์น˜ ์•Š๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊นŒ์ง€ ํฌํ•จํ•˜๊ณ , ๊ฐ’์„ ์ง€์ •ํ•ด์ค˜์•ผํ•œ๋‹ค.

์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด๋„ ์“ธ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐœ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๋ฉด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ฝ๊ธฐ ์–ด๋ ต๋‹ค.

02. ์ž๋ฐ”๋นˆ์ฆˆ ํŒจํ„ด(JavaBeans Pattern)

๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ์ƒ์„ฑ์ž๋กœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“  ํ›„, setter ๋ฉ”์„œ๋“œ๋“ค์„ ํ˜ธ์ถœํ•ด ์›ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ์‹.

Lombok์—์„œ๋„ ๋ณผ ์ˆ˜ ์žˆ๋Š” @setter. ์ฝ”๋“œ๋Š” ๊ธธ์–ด์ง€์ง€๋งŒ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์‰ฝ๊ณ , ์ด์ „ ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด๋ณด๋‹ค ์ฝ๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๊ฐ€ ๋˜์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜, ๊ฐ์ฒด ํ•˜๋‚˜๋ฅผ ๋งŒ๋“œ๋ ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๊ณ , ๊ฐ์ฒด๊ฐ€ ์™„์ „ํžˆ ์ƒ์„ฑ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ผ๊ด€์„ฑ(consistency)์ด ๋ฌด๋„ˆ์ง„ ์ƒํƒœ์— ๋†“์ด๊ฒŒ ๋œ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์ด ์œ ํšจํ•œ์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์น˜๊ฐ€ ์—†์–ด์ง„๋‹ค.

์ผ๊ด€์„ฑ์ด ๋ฌด๋„ˆ์ง€๋Š” ๋ฌธ์ œ๋กœ ํด๋ž˜์Šค๋ฅผ ๋ถˆ๋ณ€์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์œผ๋ฉฐ ์“ฐ๋ ˆ๋“œ ์•ˆ์ „์„ฑ์„ ์–ป์œผ๋ ค๋ฉด ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ถ”๊ฐ€์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.

0x. ํ”„๋ฆฌ์ง•

์ด ๋ฐฉ๋ฒ•์€ ๋‹ค๋ฃจ๊ธฐ ์–ด๋ ค์›Œ์„œ ์‹ค์ „์—์„œ๋Š” ๊ฑฐ์˜ ์“ฐ์ด์ง€ ์•Š๋Š”๋‹ค.

03. ๋นŒ๋” ํŒจํ„ด(Builder Pattern)

ํด๋ผ์ด์–ธํŠธ๋Š” ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“œ๋Š” ๋Œ€์‹ , ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋งŒ์œผ๋กœ ์ƒ์„ฑ์ž(or ์ •์ ํŒฉํ† ๋ฆฌ)๋ฅผ ํ˜ธ์ถœํ•ด ๋นŒ๋” ๊ฐ์ฒด๋ฅผ ์–ป๋Š”๋‹ค. ๊ทธ ๋‹ค์Œ์— setter๋ฅผ ์ด์šฉํ•ด ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ์„ค์ •ํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” build ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ๋“œ๋””์–ด ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ (๋ถˆ๋ณ€) ๊ฐ์ฒด๋ฅผ ์–ป๋Š”๋‹ค.

๋นŒ๋”๋Š” ์ƒ์„ฑํ•  ํด๋ž˜์Šค ์•ˆ์— ์ •์  ๋ฉค๋ฒ„ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์–ด ๋‘๋Š” ๊ฒŒ ๋ณดํ†ต์ด๋‹ค.

public class NutritionFactsBuilder {
    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 NutritionFactsBuilder build() {
            return new NutritionFactsBuilder(this);
        }
    }

    private NutritionFactsBuilder(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

ํ˜„์žฌ NutritionFactsBuilder ํด๋ž˜์Šค๋Š” ๋ถˆ๋ณ€์ด๋ฉฐ, ๋ชจ๋“  ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ๊ฐ’๋“ค์„ ํ•œ๊ณณ์— ๋ชจ์•˜๋‹ค. setter ๋ฉ”์„œ๋“œ๋“ค์€ ๋นŒ๋” ์ž์‹ ์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ์‡„์ ์œผ๋กœ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์„ ํ”Œ๋ฃจ์–ธํŠธ API, ๋ฉ”์„œ๋“œ ์—ฐ์‡„(Method Chaning) ์ด๋ผ ํ•œ๋‹ค.

NutritionFactsBuilder cola = new NutritionFactsBuilder.Builder(240, 8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27)
                .build();8)

ํ˜„์žฌ ์ด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋Š” ์“ฐ๊ธฐ ์‰ฝ๊ณ , ์ฝ๊ธฐ ์‰ฝ๋‹ค. ๋นŒ๋” ํŒจํ„ด์€ ๋ช…๋ช…๋œ ์„ ํƒ์ „ ๋งค๊ฐœ๋ณ€์ˆ˜(ํŒŒ์ด์ฌ, ์Šค์นผ๋ผ์— ์žˆ๋Š”)๋ฅผ ํ‰๋‚ด ๋‚ธ ๊ฒƒ์ด๋‹ค. ์ž˜๋ชป๋œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ผ์ฐ ๋ฐœ๊ฒฌํ•˜๋ ค๋ฉด ๋นŒ๋”์˜ ์ƒ์„ฑ์ž์™€ ๋ฉ”์„œ๋“œ์—์„œ ์ž…๋ ฅ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ , build ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ์ƒ์„ฑ์ž์—์„œ ์—ฌ๋Ÿฌ ๋งค๊ฐœ๋ณ€์ˆ˜์— ๊ฑฐ๋ฆฐ ๋ถˆ๋ณ€์‹(invariant)์„ ๊ฒ€์‚ฌํ•˜์ž.

๊ณต๊ฒฉ์— ๋Œ€๋น„ํ•ด ๋ถˆ๋ณ€์‹์„ ๋ณด์žฅํ•˜๋ ค๋ฉด ๋นŒ๋”๋กœ๋ถ€ํ„ฐ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณต์‚ฌํ•œ ํ›„ ํ•ด๋‹น ๊ฐ์ฒด ํ•„๋“œ๋“ค๋„ ๊ฒ€์‚ฌํ•ด์•ผํ•œ๋‹ค.

๋นŒ๋” ํŒจํ„ด๊ณผ ๊ณ„์ธต์  ํด๋ž˜์Šค

์ถ”์ƒ ํด๋ž˜์Šค๋Š” ์ถ”์ƒ ๋นŒ๋”๋ฅผ, ๊ตฌ์ฒด ํด๋ž˜์Šค(concrete class)๋Š” ๊ตฌ์ฒด ๋นŒ๋”๋ฅผ ๊ฐ–๊ฒŒ ํ•œ๋‹ค.

Pizza.java

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();

        // ํ•˜์œ„ ํด๋ž˜์Šค๋Š” ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜(overriding)ํ•˜์—ฌ this๋ฅผ ๋ฐ˜ํ™˜
        protected abstract T self();
    }

    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone();
    }
} 

Pizza.Builder ํด๋ž˜์Šค๋Š” ์žฌ๊ท€์  ํƒ€์ž…ํ•œ์ •์„ ์ด์šฉํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค. ์—ฌ๊ธฐ์— ์ถ”์ƒ ๋ฉ”์„œ๋“œ์ธ self()๋ฅผ ๋”ํ•ด ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ๋Š” ํ˜•๋ณ€ํ™˜ํ•˜์ง€ ์•Š๊ณ , ๋ฉ”์„œ๋“œ ์—ฐ์‡„๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

self ํƒ€์ž…์ด ์—†๋Š” ์ž๋ฐ”๋ฅผ ์œ„ํ•œ ์ด ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ์‹œ๋ฎฌ๋ ˆ์ดํŠธํ•œ ์…€ํ”„ ํƒ€์ž…(simulated self-type) ๊ด€์šฉ๊ตฌ๋ผ ํ•œ๋‹ค.

Pizza, NYPizza, CalzonePizza

๊ฐ ํ•˜์œ„ ํด๋ž˜์Šค์˜ ๋นŒ๋”๊ฐ€ ์ •์˜ํ•œ build ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹นํ•˜๋Š” ๊ตฌ์ฒด ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ ์–ธ. NYPizza.builder ๋Š” NYPizza๋ฅผ ๋ฐ˜ํ™˜, CalzonePizza.builder๋Š” CalzonePizza๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

ํ•˜์œ„ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์ƒ์œ„ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜ํ•œ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ์•„๋‹Œ, ๊ทธ ํ•˜์œ„ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ณต๋ณ€๋ฐ˜ํ™˜ ํƒ€์ดํ•‘(convariant return typing)์ด๋ผ ํ•œ๋‹ค. ์ด ์ด์šฉํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜•๋ณ€ํ™˜์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ณ ๋„ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ

NYPizza pizza = new Builder(SMALL)
                .addTopping(SAUSAGE)
                .addTopping(ONION)
                .build();

        CalzonePizza calzone = new CalzonePizza.Builder()
                .addTopping(HAM)
                .sauceInside()
                .build();

๋นŒ๋”๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ€๋ณ€์ธ์ˆ˜(varags) ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ๊ฐ์„ ์ ์ ˆํ•œ ๋ฉ”์„œ๋“œ๋กœ ๋‚˜๋ˆ  ์„ ์–ธํ•˜๋ฉด ๋œ๋‹ค. ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋„๋ก ํ•˜๊ณ  ๊ฐ ํ˜ธ์ถœ ๋•Œ ๋„˜๊ฒจ์ง„ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ํ•˜๋‚˜์˜ ํ•„๋“œ๋กœ ๋ชจ์„ ์ˆ˜ ์žˆ๋‹ค.

(์ƒ์„ฑ์ž๋‚˜ ํŒฉํ† ๋ฆฌ๋Š” ๊ฐ€๋ณ€์ธ์ž๋ฅผ ๋งจ ๋งˆ์ง€๋ง‰ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ•œ๋ฒˆ๋ฐ–์— ๋ชป์“ด๋‹ค.)

๋นŒ๋” ํŒจํ„ด์€ ์ƒ๋‹นํžˆ ์œ ์—ฐํ•˜๋‹ค. ๋นŒ๋” ํ•˜๋‚˜๋กœ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , ๋นŒ๋”์— ๋„˜๊ธฐ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋งˆ๋‹ค ๋ถ€์—ฌ๋˜๋Š” ์ผ๋ จ๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ํŠน์ • ํ•„๋“œ๋Š” ๋นŒ๋”๊ฐ€ ์•Œ์•„์„œ ์ฑ„์šฐ๋„๋ก ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋‹จ์ ์€ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋ ค๋ฉด, ๊ทธ์— ์•ž์„œ ๋นŒ๋”๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด๋ณด๋‹ค๋Š” ์ฝ”๋“œ๊ฐ€ ์žฅํ™ฉํ•ด์„œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ 4๊ฐœ ์ด์ƒ์€ ๋˜์–ด์•ผ ๊ฐ’์–ด์น˜๊ฐ€ ์žˆ๋‹ค. โ†’ API๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚ ์ˆ˜๋ก ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๊ด€ X

ํ•ต์‹ฌ์ •๋ฆฌ

์ƒ์„ฑ์ž, ์ •์ ํŒฉํ† ๋ฆฌ๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ๋นŒ๋” ํŒจํ„ด์„ ์„ ํƒํ•˜์ž.


Q&A

David

abstract static class Builder<T extends Builder> ์—์„œ ๋ณด์ด๋Š” recursive type parameter์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋นŒ๋” ํŒจํ„ด์ด ์ƒ์† ๊ด€๊ณ„์— ์–ฝํ˜€์žˆ์„ ๋•Œ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์ด๋‚˜ ๋ฌธ๋ฒ•์„ ์ดํ•ดํ•ด์•ผ ํ•  ๋“ฏ ํ•˜๋„ค์š”.

A: Recursive type parameter ์ฆ‰, ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์„ ์ด์šฉํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”์ƒ๋ฉ”์„œ๋“œ self๋ฅผ ์ง€์›ํ•˜์—ฌ ํ•˜์œ„ํด๋ž˜์Šค์—์„œ๋„ ํ˜•๋ณ€ํ™˜ ํ•˜์ง€ ์•Š๊ณ ๋„ ์ƒ์œ„ํƒ€์ž…์—์„œ ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์—ฐ์‡„์ ์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์•„์ดํ…œ30์—์„œ Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์†Œ๊ฐœํ•˜๋ฉด์„œ ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์„ ์†Œ๊ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

public interface Comparable<T> {
	int compareTo(T o);
}

public static <E extends Comparable<E>> E max(Collection<E> c);

// ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ • ๋นŒ๋”
abstract static class Builder<T extends Builder<T>> {

}

ํƒ€์ž… ํ•œ์ •์ธ <E extends Comparable<E>> ๋Š” "๋ชจ๋“  ํƒ€์ž… E๋Š” ์ž์‹ ๊ณผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค" ๋ผ๊ณ  ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด Builder<T extends Builder<T>> ๋Š” "๋ชจ๋“  ํƒ€์ž… T๋Š” ์ž์‹ ๊ณผ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค." ๋ผ๊ณ  ์ฝ์„ ์ˆ˜๋„ ์žˆ๊ฒ ์ฃ .

ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ํƒ€์ž… ์บ์ŠคํŒ…์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์„ ์ดํ•ด๋ฅผ ํ•œ ๊ฑด์ง€ ๋ชจ๋ฅด๊ฒ ๊ตฐ์š”. ๊ทธ๋Ÿฌ๋‚˜, ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์ด ์ด๋ณด๋‹ค ํ›จ์”ฌ ๋ณต์žกํ•ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋‚˜, ๋‹คํ–‰ํžˆ ๊ทธ๋Ÿฐ์ผ์€ ์ž˜ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


์ƒ์†์— ๊ด€ํ•˜์—ฌ

๋นŒ๋” ํŒจํ„ด์—์„œ ์ƒ์†์€ ์‚ฌ์‹ค ๋‹ค๋ฅธ ์ƒ์†๊ด€๊ณ„์™€ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ”ผ์ž ์˜ˆ์ œ์ฒ˜๋Ÿผ ํ”ผ์ž โ†’ NYํ”ผ์ž, ์นผ์ดˆ๋„คํ”ผ์ž๋กœ ์ƒ์†ํ•˜๊ณ  ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•  ๋ฉ”์„œ๋“œ๋Š” build()(์ธ์Šคํ„ด์Šค ์ƒ์„ฑ), self()๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ self๋Š” ์‹œ๋ฎฌ๋ ˆ์ดํŠธํ•œ ์…€ํ”„ ํƒ€์ž… ๊ด€์šฉ๊ตฌ๋ผํ•˜์—ฌ ํŒŒ์ด์ฌ, ์Šค์นผ๋ผ์—์„œ ์žˆ๋Š” selfํƒ€์ž…์„ ๊ตฌํ˜„์„ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. Java์—์„  selfํƒ€์ž…์ด ์—†๊ฑฐ๋“ ์š”.

์‹ค์ œ ๋นŒ๋”๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ๋กฌ๋ณต์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋กฌ๋ณต์—์„œ์˜ @Builder ๋Š” ํ•„์ˆ˜๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๊ณ  ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์žฌ๊ท€์  ํƒ€์ž…ํ•œ์ •์„ ์ด์šฉํ•œ ๋นŒ๋” ์ƒ์„ฑ์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ์˜ ๊ธฐ๋Šฅ๋งŒ์„ ์‚ฌ์šฉ์„ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒ๋‹ˆ๋‹ค.

๋กฌ๋ณต์„ ์ด์šฉํ•œ ๋นŒ๋” ์ƒ์†์€ ๋ถ€๋ชจ ํด๋ž˜์Šค์—์„œ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๋„˜๊ฒจ์ค„ ๋•Œ๋Š” ํฐ ๋ฌธ์ œ๋Š” ์—†์Šต๋‹ˆ๋‹ค.

@Getter
@AllArgsConstructor
public class Parent {
    private final String parentName;
    private final int parentAge;
}
 
@Getter
public class Child extends Parent {
    private final String childName;
    private final int childAge;
 
    @Builder
    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }

๊ทธ๋Ÿฌ๋‚˜, ๋งŒ์•ฝ ๋ถ€๋ชจ ํด๋ž˜์Šค์—์„œ ๋นŒ๋”๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋ฉด,

@Getter
@AllArgsConstructor
public class Parent {
    private final String parentName;
    private final int parentAge;

	  @Builder
    public Parent(String parentName, int parentAge) {
        this.parentName = parentName;
        this.parentAge = parentAge;
    }
}
 
@Getter
public class Child extends Parent {
    private final String childName;
    private final int childAge;
 
    @Builder
    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }

์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ๋นŒ๋”์ด๋ฆ„์ด ์ค‘๋ณต์ด ๋˜์–ด ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ์ค‘๋ณต๋˜์–ด ๋‚˜๋Š” ์ปดํŒŒ์ผ ์—๋Ÿฌ ๊ฐ™์€๊ฒฝ์šฐ ์ž์‹ํด๋ž˜์Šค์—์„œ ๋นŒ๋”์˜ ์ด๋ฆ„์„ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—์„  ์ž์‹ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ ์‹œ builderMethodName ์—์„œ ์ง€์ •ํ•œ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@Getter
public class Child extends Parent {
    private final String childName;
    private final int childAge;
    
    @Builder(builderMethodName = "childBuilder")
    public Child(String parentName, int parentAge, String childName, int childAge) {
        super(parentName, parentAge);
        this.childName = childName;
        this.childAge = childAge;
    }
}

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
Child child = Child.childbuilder()
  .parentName("Cloud")
  .parentAge(345)
  .childName("Sunny")
  .childAge(29)
  .build();

๋งŒ์•ฝ ์ž์‹ ์ด ๋กฌ๋ณต 1.18๋ฒ„์ „์„ ์“ฐ์‹ ๋‹ค๋ฉด @SuperBuilder ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ๋„ ๋‚˜์˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@SuperBuilder๋Š” ๋ถ€๋ชจ, ์ž์‹ํด๋ž˜์Šค ๊ตฌ๋ถ„์—†์ด ๋นŒ๋”๋ฅผ ๋งŒ๋“ค์–ด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์— ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@Getter
@SuperBuilder
public class Parent {
    // same as before...
 
@Getter
@SuperBuilder
public class Child extends Parent {
   // same as before...

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
Child child = Child.childbuilder()
  .parentName("Cloud")
  .parentAge(345)
  .childName("Sunny")
  .childAge(29)
  .build(); 

๋‹จ, @Builder ์™€ ๊ฐ™์ด ์“ธ ์ˆ˜์—†๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Henry

๋น„์Šทํ•œ ์งˆ๋ฌธ์œผ๋กœ ์ถ”์ƒ ํด๋ž˜์Šค๋กœ abstract static class Builder<T extends Builder>๋ฅผ ๋งŒ๋“ค๊ณ  ์ƒ์†ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ณผ์ •์ด ์ž˜ ์ดํ•ด๊ฐ€ ์•ˆ๊ฐ€๋Š”๋ฐ์š”.. ์ €ํฌ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋„๋ก ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ด์•ผ๊ธฐ ํ•ด๋ณด๋ฉด ์ข‹์„๊ฑฐ๊ฐ™์•„์š”.

A: ์ž๋ฐ”๋ด„์—์„œ ๋ณธ ์˜ˆ์ œ์—์„œ๋Š” ์นด๋“œ๋ฅผ ์˜ˆ์‹œ๋กœ ํ•˜์—ฌ ํ‘œํ˜„ํ–ˆ๋Š”๋ฐ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„์„ ํ•ด์•ผํ• ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. EnumSet์กด์žฌ์™€ ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •์ด ์–ด๋ ค์šด ๊ด€๊ณ„๋กœ ๊ตฌํ˜„์ด ์–ด๋ ต๋„ค์š”. ์—ฌ๊ธฐ์— ๋Œ€ํ•ด์„  ์Šคํ„ฐ๋””ํ•  ๋•Œ ๊ฐ™์ด ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆ„์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

์ž๋ฐ”๋ด„์„ ๋ฐ”ํƒ•์œผ๋กœํ•œ ๊ตฌํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

PayCard.java

public abstract class PayCard {
    public enum Benefit {
        POINT("ํฌ์ธํŠธ"), SALE("ํ• ์ธ"), SUPPORT("์—ฐํšŒ๋น„์ง€์›");
        Benefit(String benefit) {

        }
    }

    final Set<Benefit> benefits;

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Benefit> benefits = EnumSet.noneOf(Benefit.class);

        public T addBenefit(Benefit benefit) {
            this.benefits.add(benefit);
            return self();
        }

        abstract PayCard build();

        protected abstract T self();
    }

    PayCard(Builder<?> builder) {
        benefits = builder.benefits.clone();
    }

}

KakaoCard.java

public class KakaoCard extends PayCard {
    public enum Sale {
        GAME, KAKAO_STORE
    }

    private final Sale sale;

    public static class Builder extends PayCard.Builder<Builder> {
        private final Sale sale;

        public Builder(Sale sale) {
            this.sale = sale;
        }

        @Override
        public KakaoCard build() {
            return new KakaoCard(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    KakaoCard(Builder builder) {
        super(builder);
        sale = builder.sale;
    }
}

// ํด๋ผ์ด์–ธํŠธ

KakaoCard kakaoCard = new KakaoCard.Builder(GAME)
                .addBenefit(POINT)
                .build();

Han

p16 ๊ฐ์ฒด๊ฐ€ ์™„์ „ํžˆ ์ƒ์„ฑ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ผ๊ด€์„ฑ์ด ๋ฌด๋„ˆ์ง„ ์ƒํƒœ๊ฐ€ ๋œ๋‹ค..

์—ฌ๊ธฐ์„œ ๊ฐ์ฒด์˜ ์ผ๊ด€์„ฑ์ด๋ž€ ์–ด๋–ค๊ฑธ ์˜๋ฏธํ•˜๋Š” ๊ฑธ๊นŒ์š”....?> ์•„๋งˆ๋„ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๊ฐ€ setter๋ฅผ ํ†ตํ•ด ๋ณ€ํ™”๊ฐ€๋Šฅํ•จ์„ ์˜๋ฏธํ•˜์ง€ ์•Š๋‚˜ ์‹ถ๋„ค์š”. ๊ทธ ๋‹ค์Œ ๋ฌธ๋‹จ์—์„œ javabeans setter, final class๋“ฑ์ด ๋‚˜์˜ค๋Š” ๊ฑธ ๋ณด๋‹ˆ๊นŒ..

A: ๊ฐ์ฒด์˜ ์ผ๊ด€์„ฑ์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ผ๊ด€๋œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ ์ผ๊ด€๋œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ œ๊ณตํ•˜๋Š”๊ฑธ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ผ๊ด€์„ฑ์ด ์—†์œผ๋ฉด ์ ‘๊ทผ๋ฐฉ์‹(๋ณ€์ˆ˜์— ์ง์ ‘ ์‚ฌ์šฉ, ์—‘์„ธ์Šค ํ•จ์ˆ˜ ์‚ฌ์šฉ)์— ์ฃผ์˜๋ ฅ์„ ์†Œ์ง„ํ•˜๊ฒŒ ๋˜์–ด, ๋’ค์— ์žˆ๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ์— ์ฐพ๊ธฐ ํž˜๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

์ž๋ฐ”๊ฐ€ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์ด๋ผ setter๋ฅผ ์“ฐ๋ฉด ์ผ๊ด€์„ฑ์ด ๊นจ์ง€๋Š” ๊ฒŒ ๊ฐœ๋ฐœ์ž๋“ค ์ด์•ผ๊ธฐ์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•œ ์„ค๊ณ„์ด๊ณ , ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ํ•จ๊ป˜ ํ•„์ˆ˜๊ฐ’์„ ๋ชจ๋‘ ์ œ๊ณตํ•˜๊ณ , ์ถ”ํ›„์— ๋ถˆํ•„์š”ํ•œ ๊ฐ’ ๋ณ€๊ฒฝ์„ ๊ธˆ์ง€ํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ์ค‘ ๋นŒ๋” ํŒจํ„ด์€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์„ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹์„ ๋“ฏ ํ•ฉ๋‹ˆ๋‹ค.

Han์˜ ๋‹ต๋ณ€

@kses1010

์ž์‹  ์™ธ์—๋Š” ๋‚ด๋ถ€์˜ ๊ฐ€๋ณ€ ์ปดํฌ๋„ŒํŠธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋„๋ก ํ•œ๋‹ค. (์—ฌ๊ธฐ์„œ ๋ฌด์Šจ๋œป์ธ์ง€ ๋ชฐ๋ผ์„œ ๋„˜๊ฒผ์Šต๋‹ˆ๋‹ค.)

์•„๋งˆ๋„ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜์˜ ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ privateํ•˜๊ฒŒ ์„ ์–ธํ•˜๋ผ ๋ผ๋Š” ์˜๋ฏธ ๊ฐ™์•„์š”. ๋งฅ๋ฝ์ƒ ๊ฐ€๋ณ€ ์ปดํฌ๋„ŒํŠธ๋ผ๋Š” ๊ฒŒ, ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ณด๋‹ค ๋” ๋„“์€ ์˜๋ฏธ๋ผ๊ณ ๋„ ๋ณด์—ฌ์ง€๊ธด ํ•˜๋Š” ๋ฐ.. ์ œ ์ƒ๊ฐ์€ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

์Šคํ„ฐ๋”” ํ† ๋ก 

Q) ์žฌ๊ท€์  ํ•œ์ • ์ œ์–ด์ž(item30) ์— ๋Œ€ํ•ด

  • ๋Œ€๋žต์ ์ธ ๋‚ด์šฉ๋งŒ ์ด์šฉํ•˜๊ณ , Item 30๋‚˜์™”์„ ๋•Œ ๋‹ค์‹œ ํ•œ๋ฒˆ ๋ณด์ž

Q) ์ผ๊ด€์„ฑ์— ๋Œ€ํ•ด์„œ

  • Item17์— ์ข€ ๋” ์ž˜ ์ •์˜๋˜์–ด์žˆ์Œ.
  • @david215 ์ด ์ฑ…์˜ ๋…์ž๋Š” API, Library๋ฅผ ๋งŒ๋“œ๋Š” ์„ค๊ณ„์ž, ๊ฐœ๋ฐœ์ž๋“ค์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š”๋“ฏ