λ””μžμΈ νŒ¨ν„΄/GOF

πŸ’  λΉŒλ”(Builder) νŒ¨ν„΄ - μ™„λ²½ λ§ˆμŠ€ν„°ν•˜κΈ°

인파_ 2023. 3. 16. 08:06

gof-builder-pattern
gof-builder-pattern

Builder Pattern

λΉŒλ” νŒ¨ν„΄(Builder Pattern)은 λ³΅μž‘ν•œ 객체의 생성 κ³Όμ •κ³Ό ν‘œν˜„ 방법을 λΆ„λ¦¬ν•˜μ—¬ λ‹€μ–‘ν•œ κ΅¬μ„±μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 생성 νŒ¨ν„΄μ΄λ‹€. μƒμ„±μžμ— λ“€μ–΄κ°ˆ 맀개 λ³€μˆ˜λ₯Ό λ©”μ„œλ“œλ‘œ ν•˜λ‚˜ν•˜λ‚˜ 받아듀이고 λ§ˆμ§€λ§‰μ— 톡합 λΉŒλ“œν•΄μ„œ 객체λ₯Ό μƒμ„±ν•˜λŠ” 방식이닀. 

μ΄ν•΄ν•˜κΈ° μ‰¬μš΄ μ‚¬λ‘€λ‘œ 수제 햄버거λ₯Ό λ“€ 수 μžˆλ‹€. 수제 햄버거λ₯Ό μ£Όλ¬Έν• λ•Œ λΉ΅μ΄λ‚˜ νŒ¨ν‹° λ“± μ†μž¬λ£Œλ“€μ€ μ£Όλ¬Έν•˜λŠ” μ‚¬λžŒμ΄ λ§ˆμŒλŒ€λ‘œ κ²°μ •λœλ‹€. μ–΄λŠ μ‚¬λžŒμ€ 치즈λ₯Ό 빼달라고 ν• μˆ˜ 있고 μ–΄λŠ μ‚¬λžŒμ€ ν† λ§ˆν† λ₯Ό 빼달라고 ν• μˆ˜ μžˆλ‹€. 이처럼 선택적 μ†μž¬λ£Œλ“€μ„ 보닀 μœ μ—°ν•˜κ²Œ λ°›μ•„ λ‹€μ–‘ν•œ νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν• μˆ˜ μžˆμ–΄, 클래슀의 선택적 λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ€ μƒν™©μ—μ„œ μœ μš©ν•˜κ²Œ μ‚¬μš©λœλ‹€.

gof-builder-pattern


λΉŒλ” νŒ¨ν„΄ 탄생 λ°°κ²½

 

점측적 μƒμ„±μž νŒ¨ν„΄

점측적 μƒμ„±μž νŒ¨ν„΄(Telescoping Constructor Pattern)은 ν•„μˆ˜ λ§€κ°œλ³€μˆ˜μ™€ ν•¨κ»˜ 선택 λ§€κ°œλ³€μˆ˜λ₯Ό 0개, 1개, 2개 .. λ°›λŠ” ν˜•νƒœλ‘œ, μš°λ¦¬κ°€ λ‹€μ–‘ν•œ λ§€κ°œλ³€μˆ˜λ₯Ό μž…λ ₯λ°›μ•„ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ‹Άμ„λ•Œ μ‚¬μš©ν•˜λ˜ μƒμ„±μžλ₯Ό μ˜€λ²„λ‘œλ”© ν•˜λŠ” 방식이닀.

class Hamburger {
    // ν•„μˆ˜ λ§€κ°œλ³€μˆ˜
    private int bun;
    private int patty;

    // 선택 λ§€κ°œλ³€μˆ˜
    private int cheese;
    private int lettuce;
    private int tomato;
    private int bacon;

    public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato, int bacon) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
        this.tomato = tomato;
        this.bacon = bacon;
    }

    public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
        this.tomato = tomato;
    }
    

    public Hamburger(int bun, int patty, int cheese, int lettuce) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
    }

    public Hamburger(int bun, int patty, int cheese) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
    }

    ...
}
public static void main(String[] args) {
    // λͺ¨λ“  μž¬λ£Œκ°€ μžˆλŠ” 햄버거
    Hamburger hamburger1 = new Hamburger(2, 1, 2, 4, 6, 8);

    // λΉ΅κ³Ό νŒ¨ν‹° 치즈만 μžˆλŠ” 햄버거
    Hamburger hamburger2 = new Hamburger(2, 1, 1);

    // λΉ΅κ³Ό νŒ¨ν‹° 베이컨만 μžˆλŠ” 햄버거
    Hamburger hamburger3 = new Hamburger(2, 0, 0, 0, 0, 6);
}

ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ 방식은 클래슀 μΈμŠ€ν„΄μŠ€ ν•„λ“œλ“€μ΄ 많으면 λ§Žμ„ 수둝 μƒμ„±μžμ— λ“€μ–΄κ°ˆ 인자의 μˆ˜κ°€ λŠ˜μ–΄λ‚˜ λͺ‡λ²ˆμ§Έ μΈμžκ°€ μ–΄λ–€ ν•„λ“œμ˜€λŠ”μ§€ ν–‡κ°ˆλ¦΄ κ²½μš°κ°€ μƒκΈ°κ²Œ λœλ‹€. 만일 μ—¬λŸ¬ μ’…λ₯˜μ˜ 햄버거λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄ Hamburger μƒμ„±μžμ˜ λͺ‡λ²ˆμ§Έ μΈμˆ˜κ°€ μ–‘μƒμΆ”μ˜ κ°―μˆ˜μΈμ§€ ν† λ§ˆν† μ˜ κ°―μˆ˜μΈμ§€ νŒŒμ•…ν•  ν•„μš”κ°€ μžˆλ‹€.

λ˜ν•œ λ§€κ°œλ³€μˆ˜ νŠΉμ„±μƒ μˆœμ„œλ₯Ό 따라야 ν•˜κΈ° λ•Œλ¬Έμ— μœ„μ˜ 'λΉ΅κ³Ό 베이컨만 μžˆλŠ” 햄버거'λ₯Ό μ›ν• κ²½μš° μ–΅μ§€λ‘œ νŒŒλΌλ―Έν„°μ— 0을 전달해야 λœλ‹€. μƒμ„±μžλ‘œλ§ŒμœΌλ‘œλŠ” ν•„λ“œλ₯Ό μ„ νƒμ μœΌλ‘œ μƒλž΅ν•  수 μžˆλŠ” 방법이 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

그리고 무엇보닀 νƒ€μž…μ΄ λ‹€μ–‘ν•  수둝 μƒμ„±μž λ©”μ„œλ“œ μˆ˜κ°€ κΈ°ν•˜κΈ‰μˆ˜μ μœΌλ‘œ λŠ˜μ–΄λ‚˜ κ°€λ…μ„±μ΄λ‚˜ μœ μ§€λ³΄μˆ˜ μΈ‘λ©΄μ—μ„œ 쒋지 μ•Šλ‹€.

 

μžλ°” 빈(Java Beans) νŒ¨ν„΄

μ΄λŸ¬ν•œ 단점을 λ³΄μ™„ν•˜κΈ° μœ„ν•΄ Setter λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•œ μžλ°” 빈(Bean) νŒ¨ν„΄μ΄ κ³ μ•ˆ λ˜μ—ˆλ‹€. λ§€κ°œλ³€μˆ˜κ°€ μ—†λŠ” μƒμ„±μžλ‘œ 객체 생성후 Setter λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•΄ 클래슀 ν•„λ“œμ˜ μ΄ˆκΉƒκ°’μ„ μ„€μ •ν•˜λŠ” 방식이닀.

class Hamburger {
    // ν•„μˆ˜ λ§€κ°œλ³€μˆ˜
    private int bun;
    private int patty;

    // 선택 λ§€κ°œλ³€μˆ˜
    private int cheese;
    private int lettuce;
    private int tomato;
    private int bacon;
    
    public Hamburger() {}

    public void setBun(int bun) {
        this.bun = bun;
    }

    public void setPatty(int patty) {
        this.patty = patty;
    }

    public void setCheese(int cheese) {
        this.cheese = cheese;
    }

    public void setLettuce(int lettuce) {
        this.lettuce = lettuce;
    }

    public void setTomato(int tomato) {
        this.tomato = tomato;
    }

    public void setBacon(int bacon) {
        this.bacon = bacon;
    }
}
public static void main(String[] args) {
    // λͺ¨λ“  μž¬λ£Œκ°€ μžˆλŠ” 햄버거
    Hamburger hamburger1 = new Hamburger();
    hamburger1.setBun(2);
    hamburger1.setPatty(1);
    hamburger1.setCheese(2);
    hamburger1.setLettuce(4);
    hamburger1.setTomato(6);
    hamburger1.setBacon(8);

    // λΉ΅κ³Ό νŒ¨ν‹° 치즈만 μžˆλŠ” 햄버거
    Hamburger hamburger2 = new Hamburger();
    hamburger2.setBun(2);
    hamburger2.setPatty(1);
    hamburger2.setCheese(2);

    // λΉ΅κ³Ό νŒ¨ν‹° 베이컨만 μžˆλŠ” 햄버거
    Hamburger hamburger3 = new Hamburger();
    hamburger3.setBun(2);
    hamburger2.setPatty(1);
    hamburger3.setBacon(8);
}

κΈ°μ‘΄ μƒμ„±μž μ˜€λ²„λ‘œλ”©μ—μ„œ λ‚˜νƒ€λ‚¬λ˜ 가독성 문제점이 사라지고 선택적인 νŒŒλΌλ―Έν„°μ— λŒ€ν•΄ ν•΄λ‹Ήλ˜λŠ” Setter λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•¨μœΌλ‘œμ¨ μœ μ—°μ μœΌλ‘œ 객체 생성이 κ°€λŠ₯ν•΄μ‘Œλ‹€. ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ 방식은 객체 생성 μ‹œμ μ— λͺ¨λ“  값듀을 μ£Όμž… ν•˜μ§€ μ•Šμ•„ 일관성(consistency) λ¬Έμ œμ™€ λΆˆλ³€μ„±(immutable) λ¬Έμ œκ°€ λ‚˜νƒ€λ‚˜κ²Œ λœλ‹€.

 

1) 일관성 문제

ν•„μˆ˜ λ§€κ°œλ³€μˆ˜λž€ 객체가 μ΄ˆκΈ°ν™”λ λ•Œ λ°˜λ“œμ‹œ μ„€μ •λ˜μ–΄μ•Ό ν•˜λŠ” 값이닀. ν•˜μ§€λ§Œ κ°œλ°œμžκ°€ κΉœλΉ‘ν•˜κ³  setBun() μ΄λ‚˜ setPatty() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•Šμ•˜λ‹€λ©΄ 이 κ°μ²΄λŠ” 일관성이 λ¬΄λ„ˆμ§„ μƒνƒœκ°€ λœλ‹€. 즉, 객체가 μœ νš¨ν•˜μ§€ μ•Šμ€ 것이닀. 만일 λ‹€λ₯Έκ³³μ—μ„œ 햄버거 μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜κ²Œ λœλ‹€λ©΄ λŸ°νƒ€μž„ μ˜ˆμ™Έκ°€ λ°œμƒν•  μˆ˜λ„ μžˆλ‹€.

μ΄λŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” λΆ€λΆ„κ³Ό 값을 μ„€μ •ν•˜λŠ” 뢀뢄이 물리적으둜 λ–¨μ–΄μ Έ μžˆμ–΄μ„œ λ°œμƒν•˜λŠ” λ¬Έμ œμ μ΄λ‹€. λ¬Όλ‘  μ΄λŠ” μ–΄λŠμ •λ„ μƒμ„±μž(Constructor)와 κ²°ν•©ν•˜μ—¬ 극볡은 ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ λ‹€μŒμ— μ†Œκ°œν•  λΆˆλ³€μ„±μ˜ 문제 λ•Œλ¬Έμ— μžλ°” 빈즈 νŒ¨ν„΄μ€ 지양해야 ν•œλ‹€.

 

2) λΆˆλ³€μ„± 문제

μžλ°” 빈즈 νŒ¨ν„΄μ˜ Setter λ©”μ„œλ“œλŠ” 객체λ₯Ό 처음 μƒμ„±ν• λ•Œ ν•„λ“œκ°’μ„ μ„€μ •ν•˜κΈ° μœ„ν•΄ μ‘΄μž¬ν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€. ν•˜μ§€λ§Œ 객체λ₯Ό μƒμ„±ν–ˆμŒμ—λ„ μ—¬μ „νžˆ μ™ΈλΆ€μ μœΌλ‘œ Setter λ©”μ†Œλ“œλ₯Ό λ…ΈμΆœν•˜κ³  μžˆμœΌλ―€λ‘œ, ν˜‘μ—… κ³Όμ •μ—μ„œ μ–Έμ œ μ–΄λ””μ„œ λˆ„κ΅°κ°€ Setter λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄ ν•¨λΆ€λ‘œ 객체λ₯Ό μ‘°μž‘ν• μˆ˜ 있게 λœλ‹€. 이것을 λΆˆλ³€ν•¨μ„ 보μž₯ν•  수 μ—†λ‹€κ³  μ–˜κΈ°ν•œλ‹€.

마치 μ™„μ„±λœ 햄버거에 쀑간에 치즈λ₯Ό κ΅μ²΄ν•œλ‹€κ³  햄버거λ₯Ό 막 λΆ„λ¦¬ν•˜λŠ” 것과 같은 μ΄μΉ˜μ΄λ‹€ (μž…λ§› λ–¨μ–΄μ§€κ²Œ)

 

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

λΉŒλ” νŒ¨ν„΄μ€ μ΄λŸ¬ν•œ λ¬Έμ œλ“€μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ³„λ„μ˜ Builder 클래슀λ₯Ό λ§Œλ“€μ–΄ λ©”μ†Œλ“œλ₯Ό 톡해 step-by-step 으둜 값을 μž…λ ₯받은 후에 μ΅œμ’…μ μœΌλ‘œ build() λ©”μ†Œλ“œλ‘œ ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ—¬ λ¦¬ν„΄ν•˜λŠ” νŒ¨ν„΄μ΄λ‹€.

λΉŒλ” νŒ¨ν„΄ μ‚¬μš©λ²•μ„ μž μ‹œ μ‚΄νŽ΄λ³΄λ©΄, StudentBuilder λΉŒλ” 클래슀의 λ©”μ„œλ“œλ₯Ό 체이닝(Chaining) ν˜•νƒœλ‘œ ν˜ΈμΆœν•¨μœΌλ‘œμ¨ μžμ—°μŠ€λŸ½κ²Œ μΈμŠ€ν„΄μŠ€λ₯Ό κ΅¬μ„±ν•˜κ³  λ§ˆμ§€λ§‰μ— build() λ©”μ„œλ“œλ₯Ό 톡해 μ΅œμ’…μ μœΌλ‘œ 객체λ₯Ό μƒμ„±ν•˜λ„λ‘ λ˜μ–΄μžˆμŒμ„ λ³Ό 수 μžˆλ‹€.

public static void main(String[] args) {

    // μƒμ„±μž 방식
    Hamburger hamburger = new Hamburger(2, 3, 0, 3, 0, 0);

    // λΉŒλ” 방식
    Hamburger hamburger = new Hamburger.Builder(10)
        .bun(2)
        .patty(3)
        .lettuce(3)
        .build();
}

λΉŒλ” νŒ¨ν„΄μ„ μ΄μš©ν•˜λ©΄ 더이상 μƒμ„±μž μ˜€λ²„λ‘œλ”© μ—΄κ±°λ₯Ό ν•˜μ§€ μ•Šμ•„λ„ 되며, λ°μ΄ν„°μ˜ μˆœμ„œμ— 상관없이 객체λ₯Ό λ§Œλ“€μ–΄λ‚΄ μƒμ„±μž 인자 μˆœμ„œλ₯Ό νŒŒμ•…ν•  ν•„μš”λ„ μ—†κ³  잘λͺ»λœ 값을 λ„£λŠ” μ‹€μˆ˜λ„ ν•˜μ§€ μ•Šκ²Œ λœλ‹€. 점측적 μƒμ„±μž νŒ¨ν„΄κ³Ό μžλ°”λΉˆμ¦ˆ νŒ¨ν„΄ 두 κ°€μ§€μ˜ μž₯μ λ§Œμ„ μ·¨ν•˜μ˜€λ‹€κ³  λ³Ό 수 μžˆλ‹€.


λΉŒλ” νŒ¨ν„΄ ꡬ쑰

λΉŒλ” νŒ¨ν„΄ κ΅¬ν˜„ μžμ²΄λŠ” λ‚œμ΄λ„κ°€ 어렡지 μ•ŠμœΌλ‹ˆ λΉ λ₯΄κ³  μ‰½κ²Œ ꡬ성이 κ°€λŠ₯ν•˜λ‹€.

예λ₯Όλ“€μ–΄ λ‹€μŒκ³Ό 같은 Student ν΄λž˜μŠ€μ— λŒ€ν•œ κ°μ²΄ μƒμ„±λ§Œμ„ λ‹΄λ‹Ήν•˜λŠ” λ³„λ„μ˜ λΉŒλ” 클래슀λ₯Ό λ§Œλ“€λ €κ³  ν•œλ‹€.

class Student {
    private int id;
    private String name = "μ•„λ¬΄κ°œ";
    private String grade = "freshman";
    private String phoneNumber = "010-0000-0000";

    public Student(int id, String name, String grade, String phoneNumber) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
    
    @Override
    public String toString() {
        return "Student { " +
                "id='" + id + '\'' +
                ", name=" + name +
                ", grade=" + grade +
                ", phoneNumber=" + phoneNumber +
                " }";
    }
}

 

λΉŒλ” 클래슀 κ΅¬ν˜„ν•˜κΈ°

λ¨Όμ € Builder 클래슀λ₯Ό λ§Œλ“€κ³  ν•„λ“œ 멀버 ꡬ성을 λ§Œλ“€κ³ μž ν•˜λŠ” Student 클래슀 멀버 ꡬ성과 λ˜‘κ°™μ΄ κ΅¬μ„±ν•œλ‹€. 

class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;
    
}

그리고 각 λ§΄λ²„μ—λŒ€ν•œ Setter λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•΄μ€€λ‹€. μ΄λ•Œ 가독성을 μ’‹κ²Œ ν•˜λ©΄μ„œλ„ κΈ°μ‘΄ Setterμ™€μ˜ λ‹€λ₯Έ νŠΉμ„±μ„ 가지고 μžˆλŠ” 점을 μ•Œλ¦¬κΈ° μœ„ν•΄μ„œ, set λ‹¨μ–΄λŠ” λΉΌμ£Όκ³  μ‹¬ν”Œν•˜κ²Œ λ©€λ²„μ΄λ¦„μœΌλ‘œλ§Œ λ©”μ„œλ“œλͺ…을 지어쀀닀.

class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder id(int id) {
        this.id = id;
        return this;
    }

    public StudentBuilder name(String name) {
        this.name = name;
        return this;
    }

    public StudentBuilder grade(String grade) {
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }
}

μ—¬κΈ°μ„œ μ£Όλͺ©ν•  뢀뢄은 각 Setter ν•¨μˆ˜ λ§ˆμ§€λ§‰ λ°˜ν™˜ ꡬ문인 return this λΆ€λΆ„이닀. μ—¬κΈ°μ„œ thisλž€ StudentBuilder 객체 μžμ‹ μ„ λ§ν•œλ‹€. 즉, λΉŒλ” 객체 μžμ‹ μ„ λ¦¬ν„΄ν•¨μœΌλ‘œμ¨ λ©”μ„œλ“œ 호좜 ν›„ μ—°μ†μ μœΌλ‘œ λΉŒλ” λ©”μ„œλ“œλ“€μ„ 체이닝(Chaining) ν•˜μ—¬ ν˜ΈμΆœν•  수 있게 λœλ‹€. ex) new StudentBuilder().id(κ°’).name(κ°’)

λ§ˆμ§€λ§‰μœΌλ‘œ λΉŒλ”μ˜ λͺ©ν‘œμ˜€λ˜ μ΅œμ’… Student 객체λ₯Ό λ§Œλ“€μ–΄μ£ΌλŠ” build λ©”μ„œλ“œλ₯Ό ꡬ성해쀀닀. λΉŒλ” 클래슀의 ν•„λ“œλ“€μ„ Student μƒμ„±μžμ˜ μΈμžμ— λ„£μ–΄μ€ŒμœΌλ‘œμ¨ 멀버 ꡬ성이 μ™„λ£Œλœ Student μΈμŠ€ν„΄μŠ€λ₯Ό μ–»κ²Œ λ˜λŠ” 것이닀.

class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder id(int id) { ... }

    public StudentBuilder name(String name) { ... }

    public StudentBuilder grade(String grade) { ... }

    public StudentBuilder phoneNumber(String phoneNumber) { ... }

    public Student build() {
        return new Student(id, name, grade, phoneNumber); // Student μƒμ„±μž 호좜
    }
}

 

λΉŒλ” 클래슀 μ‹€ν–‰ν•˜κΈ°

μ΄λ ‡κ²Œ κ΅¬μ„±ν•œ λΉŒλ” 객체λ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같은 μ„±μ§ˆμ˜ μ½”λ“œκ°€ κ΅¬ν˜„λ˜κ²Œ λœλ‹€.

public static void main(String[] args) {

    Student student = new StudentBuilder()
                .id(2016120091)
                .name("μž„κΊ½μ •")
                .grade("Senior")
                .phoneNumber("010-5555-5555")
                .build();

    System.out.println(student);
}

λΉŒλ” νŒ¨ν„΄


λΉŒλ” νŒ¨ν„΄ λ„€μ΄λ° ν˜•μ‹

λΉŒλ” νŒ¨ν„΄μ˜ 멀버 μ„€μ • λ©”μ„œλ“œ 넀이밍 λ°©μ‹μ—λŠ” λŒ€ν‘œμ μœΌλ‘œ 3가지 정도 μ‘΄μž¬ν•œλ‹€.

  1. 멀버이름()
  2. set멀버이름()
  3. with멀버이름()
Student student = new StudentBuilder(2016120091)
        .name("홍길동")
        .grade("freshman")
        .phoneNumber("010-5555-5555")
        .build();
Student student = new StudentBuilder(2016120091)
        .setName("홍길동")
        .setGrade("freshman")
        .setPhoneNumber("010-5555-5555")
        .build();
Student student = new StudentBuilder(2016120091)
        .withName("홍길동")
        .withGrade("freshman")
        .withPhoneNumber("010-5555-5555")
        .build();

이쀑 κ·Έλƒ₯ λ©€λ²„μ΄λ¦„μœΌλ‘œλ§Œ λ©”μ„œλ“œλͺ…을 μ§“λŠ” 첫번째 넀이밍 방식이 μΆ”μ²œλ˜μ–΄μ§„λ‹€.

λ‘λ²ˆμ§Έ 넀이밍 방식은 정톡적인 μžλ°”(Java) 슀러운 넀이밍 ν˜•μ‹μΈλ° 일반 Setter λ©”μ†Œλ“œμ™€ 햇깔릴 μ†Œμ§€κ°€ μžˆλ‹€.

μ„Έλ²ˆμ§ΈλŠ” Setter와 ꡬ뢄킀 μœ„ν•΄ 'with' λΌλŠ” ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œκ²ƒμ΄λ©°, λΉŒλ” 지연 생성 λ°©μ‹μ—μ„œ 미리 λΉŒλ”λ₯Ό μ„€μ •ν• λ•Œ 쓰이기도 ν•œλ‹€.


Builder νŒ¨ν„΄ μž₯단점 총정리 

 

λΉŒλ” νŒ¨ν„΄ μž₯점

 

1. 객체 μƒμ„± 과정을 μΌκ΄€λœ ν”„λ‘œμ„ΈμŠ€λ‘œ ν‘œν˜„

μƒμ„±μž λ°©μ‹μœΌλ‘œ 객체λ₯Ό μƒμ„±ν•˜λŠ” κ²½μš°λŠ” λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ•„μ§ˆμˆ˜λ‘ 가독성이 κΈ‰κ²©ν•˜κ²Œ 떨어진닀. 클래슀 λ³€μˆ˜κ°€ 4개 μ΄μƒλ§Œ λ˜μ–΄λ„ 각 인자 μˆœμ„œ λ§ˆλ‹€ 이 값이 μ–΄λ–€ 멀버에 ν•΄λ‹Ήλ˜λŠ”μ§€ λ°”λ‘œ νŒŒμ•…μ΄ νž˜λ“€λ‹€. 

반면 λ‹€μŒκ³Ό 같이 λΉŒλ” νŒ¨ν„΄μ„ μ μš©ν•˜λ©΄ μ§κ΄€μ μœΌλ‘œ μ–΄λ–€ 데이터에 μ–΄λ–€ 값이 μ„€μ •λ˜λŠ”μ§€ ν•œλˆˆμ— νŒŒμ•…ν•  수 있게 λœλ‹€. 특히 μ—°μ†λœ 동일 νƒ€μž…μ˜ 맀개 λ³€μˆ˜λ₯Ό 많이 μ„€μ •ν•  κ²½μš°μ— λ°œμƒν•  수 μžˆλŠ” μ„€μ • 였λ₯˜μ™€ 같은 μ‹€μˆ˜λ₯Ό 방지할 수 μžˆλ‹€.

// μƒμ„±μž 방식
Student student1 = new Student(2016120091, "홍길동", "freshman", "010-5555-5555");

// λΉŒλ” 방식
Student student2 = new StudentBuilder()
            .id(2016120091)
            .name("μž„κΊ½μ •")
            .grade("Senior")
            .phoneNumber("010-5555-5555")
            .build();

λ‹€λ§Œ λΉŒλ” νŒ¨ν„΄μ΄ νƒ„μƒν•˜κ²Œλœ μ˜› μ‹œλŒ€μ™€ 달리, μš”μ¦˜μ—λŠ” μΈν…”λ¦¬μ œμ΄λ‚˜ 이클립슀 같은 μ™ λ§Œν•œ IDE에선 μ•„λž˜μ™€ 같이 μƒμ„±μž λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ 미리보기 힌트 κΈ°λŠ₯을 μ œκ³΅ν•΄μ£ΌκΈ° λ•Œλ¬Έμ— 이 뢀뢄은 μš”μ¦˜ νŠΈλ Œλ“œμ—λŠ” λ§žμ§€ μ•Šμ„ μˆ˜λ„ μžˆλ‹€.

builder-pattern

 

2. λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜ μƒλž΅μ„ κ°„μ ‘μ μœΌλ‘œ 지원

본래 λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λΌλŠ” 건 인자 값을 μ„€μ •ν•΄μ€˜λ„ 되고 μ„€μ • μ•ˆν•˜κ³  μƒλž΅ν•΄λ„ λ˜λŠ”κ²ƒμ„ λ§ν•œλ‹€. 그런데 νŒŒμ΄μ¬μ΄λ‚˜ μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ 달리 μžλ°” 언어에선 기본적으둜 λ©”μ„œλ“œμ— λŒ€ν•œ λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.

builder-pattern

λ”°λΌμ„œ λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λ₯Ό κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„  클래슀 ν•„λ“œ λ³€μˆ˜μ— μ΄ˆκΉƒκ°’μ„ 미리 μ„ΈνŒ…ν•˜κ³ , μ΄ˆκΉƒκ°’μ΄ μ„ΈνŒ…λœ ν•„λ“œ 인자λ₯Ό μ œμ™Έμ‹œν‚¨ μƒμ„±μžλ₯Ό λ”°λ‘œ κ΅¬ν˜„ν•˜λŠ” μ‹μœΌλ‘œ 섀계해야 ν•œλ‹€. ν•˜μ§€λ§Œ μ΄λŠ” κ²°κ΅­ μ§€λ‚˜μΉœ μƒμ„±μž μ˜€λ²„λ‘œλ”© μ—΄κ±°λ₯Ό ν†΅ν•œ 본래의 λ¬Έμ œμ μ„ νšŒκ·€ν•œ 꼴이 λœλ‹€.

class Student {
    private int id;
    private String name;
    private String grade = "freshman"; // λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜ μ—­ν• 
    private String phoneNumber;

    public Student(int id, String name, String grade, String phoneNumber) {
        ...
    }
	
    // λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λ₯Ό μ œμ™Έν•œ μΈμžλ“€μ„ λ°›λŠ” μƒμ„±μž μ˜€λ²„λ‘œλ”©
    public Student(int id, String name, String phoneNumber) {
        ...
    }

    @Override
    public String toString() {
        return "Student { " +
                "id='" + id + '\'' +
                ", name=" + name +
                ", grade=" + grade +
                ", phoneNumber=" + phoneNumber +
                " }";
    }
}

λΉŒλ” νŒ¨ν„΄μ—μ„œλ„ λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법은 λ˜‘κ°™λ‹€. λ‹€λ§Œ λΉŒλ”λΌλŠ” 객체 생성 μ „μš© 클래슀λ₯Ό κ²½μœ ν•˜μ—¬ μ΄μš©ν•¨μœΌλ‘œμ¨ λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜κ°€ μ„€μ •λœ ν•„λ“œλ₯Ό μ„€μ •ν•˜λŠ” λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•ŠλŠ” λ°©μ‹μœΌλ‘œ 마치 λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜λ₯Ό μƒλž΅ν•˜κ³  ν˜ΈμΆœν•˜λŠ” 효과λ₯Ό κ°„μ ‘μ μœΌλ‘œ κ΅¬ν˜„ν• μˆ˜ 있게 λœλ‹€.

class StudentBuilder {
    private int id;
    private String name;
    private String grade = "freshman"; // λ””ν΄νŠΈ λ§€κ°œλ³€μˆ˜ μ—­ν• 
    private String phoneNumber;

    ...
}
// λ””ν΄νŠΈ ν•„λ“œμΈ gradeλ₯Ό μ œμ™Έν•˜κ³  λΉŒλ” ꡬ성 및 μΈμŠ€ν„΄μŠ€ν™”
Student student1 = new StudentBuilder(2016120091)
        .name("홍길동")
        .phoneNumber("010-5555-5555")
        .build();

System.out.println(student1);

builder-pattern

 

3. ν•„μˆ˜ 멀버와 선택적 멀버λ₯Ό 뢄리 κ°€λŠ₯

객체 μΈμŠ€ν„΄μŠ€λŠ” λͺ©μ μ— 따라 μ΄ˆκΈ°ν™”κ°€ ν•„μˆ˜μΈ 멀버 λ³€μˆ˜κ°€ 있고 선택적인 멀버 λ³€μˆ˜κ°€ μžˆμ„ 수 μžˆλ‹€.

만일 Student 클래슀의 id ν•„λ“œκ°€ μΈμŠ€ν„΄μŠ€ν™” ν• λ•Œ λ°˜λ“œμ‹œ ν•„μˆ˜μ μœΌλ‘œ 값을 지정해 μ£Όμ–΄μ•Ό ν•˜λŠ” ν•„μˆ˜ 멀버 λ³€μˆ˜λΌκ³  κ°€μ •ν•΄λ³΄μž. 이λ₯Ό κΈ°μ‘΄ μƒμ„±μž λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•˜λ €λ©΄ μ΄ˆκΈ°ν™”κ°€ ν•„μˆ˜μΈ 멀버 λ³€μˆ˜λ§Œμ„ μœ„ν•œ μƒμ„±μžλ₯Ό μ •μ˜ν•˜κ³  선택적인 멀버 λ³€μˆ˜μ— λŒ€μ‘ν•˜λŠ” μƒμ„±μžλ₯Ό μ˜€λ²„λ‘œλ”©μ„ 톡해 μ—΄κ±°ν•˜κ±°λ‚˜, ν˜Ήμ€ 전체 멀버λ₯Ό 인자둜 λ°›λŠ” μƒμ„±μžλ§Œμ„ μ„ μ–Έν•˜κ³  λ§€κ°œλ³€μˆ˜μ— null을 λ°›λŠ”μ‹μœΌλ‘œ κ΅¬μ„±ν•˜μ—¬μ•Ό ν•œλ‹€.

class Student {
    // μ΄ˆκΈ°ν™” ν•„μˆ˜ 멀버
    private int id;

    // μ΄ˆκΈ°ν™” 선택적 멀버
    private String name;
    private String grade;
    private String phoneNumber;

    public Student(int id, String name, String grade, String phoneNumber) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
}
Student student = new Student(2010234455, null, null, null);

μ΄λŠ” ν•œλˆˆμ— 봐도 λ³„λ‘œ 쒋지 μ•Šμ€ λ°©λ²•μž„μ„ μ•Œμˆ˜ μžˆμ„ 것이닀.

λ”°λΌμ„œ λΉŒλ” 클래슀λ₯Ό 톡해 μ΄ˆκΈ°ν™”κ°€ ν•„μˆ˜μΈ λ©€λ²„λŠ” λΉŒλ”μ˜ μƒμ„±μžλ‘œ λ°›κ²Œ ν•˜μ—¬ ν•„μˆ˜ 멀버λ₯Ό μ„€μ •ν•΄μ£Όμ–΄μ•Ό λΉŒλ” 객체가 μƒμ„±λ˜λ„λ‘ μœ λ„ν•˜κ³ , 선택적인 λ©€λ²„λŠ” λΉŒλ”μ˜ λ©”μ„œλ“œλ‘œ 받도둝 ν•˜λ©΄, μ‚¬μš©μžλ‘œ ν•˜μ—¬κΈˆ ν•„μˆ˜ 멀버와 선택 멀버λ₯Ό κ΅¬λΆ„ν•˜μ—¬ 객체 생성을 μœ λ„ν•  수 μžˆλ‹€.

class StudentBuilder {
    // μ΄ˆκΈ°ν™” ν•„μˆ˜ 멀버
    private int id;

    // μ΄ˆκΈ°ν™” 선택적 멀버
    private String name;
    private String grade;
    private String phoneNumber;

    // ν•„μˆ˜ λ©€λ²„λŠ” λΉŒλ”μ˜ μƒμ„±μžλ₯Ό 톡해 μ„€μ •
    public StudentBuilder(int id) {
        this.id = id;
    }

    // λ‚˜λ¨Έμ§€ 선택 λ©€λ²„λŠ” λ©”μ„œλ“œλ‘œ μ„€μ •
    public StudentBuilder name(String name) {
        this.name = name;
        return this;
    }

    public StudentBuilder grade(String grade) {
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }

    public Student build() {
        return new Student(id, name, grade, phoneNumber);
    }
}
Student student1 = 
        new StudentBuilder(2016120091) // ν•„μˆ˜ 멀버
        .name("홍길동") // 선택 멀버
        .build();

Student student2 = 
        new StudentBuilder(2016120091) // ν•„μˆ˜ 멀버
        .name("μž„κΊ½μ •") // 선택 멀버
        .grade("freshman") // 선택 멀버
        .build();

Student student3 = 
        new StudentBuilder(2016120091) // ν•„μˆ˜ 멀버
        .name("μ£Όλͺ½") // 선택 멀버
        .grade("Senior") // 선택 멀버
        .phoneNumber("010-5555-5555") // 선택 멀버
        .build();

 

4. 객체 생성 단계λ₯Ό 지연할 수 있음

객체 생성을 λ‹¨κ³„λ³„λ‘œ κ΅¬μ„±ν•˜κ±°λ‚˜ ꡬ성 단계λ₯Ό μ§€μ—°ν•˜κ±°λ‚˜ μž¬κ·€μ μœΌλ‘œ 생성을 처리 ν• μˆ˜ μžˆλ‹€. 즉, λΉŒλ”λ₯Ό μž¬μ‚¬μš© ν•¨μœΌλ‘œμ¨ 객체 생성을 μ£Όλ„μ μœΌλ‘œ 지연할 수 μžˆλŠ” 것이닀.

// 1. λΉŒλ” 클래슀 μ „μš© 리슀트 생성
List<StudentBuilder> builders = new ArrayList<>();

// 2. 객체λ₯Ό μ΅œμ’… 생성 ν•˜μ§€λ§κ³  μ΄ˆκΉƒκ°’λ§Œ μ„ΈνŒ…ν•œ λΉŒλ”λ§Œ 생성
builders.add(
    new StudentBuilder(2016120091)
    .name("홍길동")
);

builders.add(
    new StudentBuilder(2016120092)
    .name("μž„κΊ½μ •")
    .grade("senior")
);

builders.add(
    new StudentBuilder(2016120093)
    .name("λ°•ν˜κ±°μ„Έ")
    .grade("sophomore")
    .phoneNumber("010-5555-5555")
);

// 3. λ‚˜μ€‘μ— λΉŒλ” 리슀트λ₯Ό μˆœνšŒν•˜μ—¬ μ΅œμ’… 객체 생성을 주도
for(StudentBuilder b : builders) {
    Student student = b.build();
    System.out.println(student);
}

builder-pattern

 

6. μ΄ˆκΈ°ν™” 검증을 λ©€λ²„λ³„λ‘œ 뢄리

만일 μƒμ„±μžλ‘œ λΆ€ν„° 멀버값을 λ°›λŠ” ν˜•νƒœλΌλ©΄, 각 μƒμ„±μž λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ 검증 λ‘œμ§μ„ μƒμ„±μž λ©”μ†Œλ“œ λ§ˆλ‹€ λ³΅μž‘ν•˜κ²Œ κ΅¬ν˜„ν•˜μ—¬μ•Ό ν•œλ‹€. μ΄λŠ” μƒμ„±μžμ˜ 크기가 λΉ„λŒ€ν•΄μ§€κ²Œ λ˜λŠ” κ²°κ³Όλ₯Ό λ‚³κ²Œ λœλ‹€.

class Student {
	...

	// 각 λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ 검증을 ν•˜λ‚˜μ˜ μƒμ„±μž λͺ¨λ‘ μ²˜λ¦¬ν•˜κ³  μžˆλ‹€
    public Student(int id, String name, String grade, String phoneNumber) {
        if (!grade.equals("freshman") && !grade.equals("sophomore") && !grade.equals("junior") && !grade.equals("senior")) {
            throw new IllegalArgumentException(grade);
        }

        if (!phoneNumber.startsWith("010")) {
            throw new IllegalArgumentException(phoneNumber);
        }

        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
}

λΉŒλ”λ₯Ό μ΄μš©ν•˜λ©΄ 생성될 객체의 멀버 λ³€μˆ˜μ˜ μ΄ˆκΈ°ν™”μ™€ 검증을 각각의 λ©€λ²„λ³„λ‘œ λΆ„λ¦¬ν•΄μ„œ μž‘μ„±ν•  수 μžˆλ‹€. λΉŒλ”μ˜ κ°κ°μ˜λ©€λ²„ μ„€μ • λ©”μ„œλ“œμ—μ„œ 검증 과정을 λΆ„λ‹΄ν•¨μœΌλ‘œμ¨ μœ μ§€ 보수λ₯Ό μš©μ΄ν•˜κ²Œ ν•˜λŠ” 것이닀.

class StudentBuilder {
	...

    public StudentBuilder(int id) {
        this.id = id;
    }

    public StudentBuilder name(String name) {
        this.name = name;
        return this;
    }

    public StudentBuilder grade(String grade) {
        if (!grade.equals("freshman") && !grade.equals("sophomore") && !grade.equals("junior") && !grade.equals("senior")) {
            throw new IllegalArgumentException(grade);
        }
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        if (!phoneNumber.startsWith("010")) {
            throw new IllegalArgumentException(phoneNumber);
        }
        this.phoneNumber = phoneNumber;
        return this;
    }

    public Student build() {
        return new Student(id, name, grade, phoneNumber);
    }
}

λ¬Όλ‘  μ΄λŸ¬ν•œ ν˜•μ‹μ€ ν”νžˆ Getter & Setter ν˜•μ‹μ—μ„œλ„ 많이 μ΄μš©λ˜λŠ” νŒ¨ν„΄μ΄κΈ°λ„ ν•˜λ‹€. ν•˜μ§€λ§Œ μ–΄λŠ ν΄λž˜μŠ€μ— Setter λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•œλ‹€λŠ” 말은 객체 λ©€λ²„μ˜ λ³€κ²½ κ°€λŠ₯성을 열어둔것과 κ°™μ•„ λΆˆλ³€μ„± λ¬Έμ œκ°€ ν„°μ§€κ²Œ λœλ‹€. (λ°”λ‘œ λ‹€μŒμ— μ„€λͺ…)

 

7. λ©€λ²„에 λŒ€ν•œ λ³€κ²½ κ°€λŠ₯μ„± μ΅œμ†Œν™”λ₯Ό 좔ꡬ

λ§Žμ€ κ°œλ°œμžλ“€μ΄ μžλ°” ν”„λ‘œκ·Έλž˜λ°μ„ ν•˜λ©΄μ„œ 멀버에 값을 ν• λ‹Ήν• λ•Œ ν”νžˆ μ‚¬μš©ν•˜λŠ” 것이 Setter λ©”μ„œλ“œμΈλ°, 그쀑 ν΄λž˜μŠ€ 맴버 μ΄ˆκΈ°ν™”λ₯Ό Setter을 톡해 κ΅¬μ„±ν•˜λŠ” 것은 λ§€μš° 쒋지 μ•Šμ€ 방법이닀. 즉, 이 뢀뢄은 μœ„μ˜ λΉŒλ” νŒ¨ν„΄ 탄생 λ°°κ²½μ—μ„œ λ‹€λ£¨μ—ˆλ˜ Java Beans Pattern인 Setter λ©”μ„œλ“œλ₯Ό 톡해 멀버 μ΄ˆκΈ°ν™”λ₯Ό ν•˜μ§€ 말아야 ν•˜λŠ” μ΄μœ μ— λŒ€ν•œ μ’€ 더 κ³ μˆ˜μ€€ 적인 λ‚΄μš©μ΄λ‹€.

 

일반적으둜 ν”„λ‘œκ·Έλž¨μ„ κ°œλ°œν•˜λŠ”λ° μžˆμ–΄ λ‹€λ₯Έ μ‚¬λžŒκ³Ό ν˜‘μ—…ν• λ•Œ κ°€μž₯ μ€‘μš”μ‹œλ˜λŠ” 점 쀑 ν•˜λ‚˜κ°€ λ°”λ‘œ λΆˆλ³€(immutalbe) 객체이닀. λΆˆλ³€ κ°μ²΄λž€ 객체 생성 이후 λ‚΄λΆ€μ˜ μƒνƒœκ°€ λ³€ν•˜μ§€ μ•ŠλŠ” 객체이닀. λΆˆλ³€ κ°μ²΄λŠ” μ˜€λ‘œμ§€ 읽기(get) λ©”μ†Œλ“œλ§Œμ„ μ œκ³΅ν•˜λ©° μ“°κΈ°(set)λŠ” μ œκ³΅ν•˜μ§€ μ•ŠλŠ”λ‹€. λŒ€ν‘œμ μœΌλ‘œ μžλ°”μ—μ„œ final ν‚€μ›Œλ“œλ₯Ό 뢙인 λ³€μˆ˜κ°€ λ°”λ‘œ λΆˆλ³€μ΄λ‹€. 

ν˜„μ—…μ—μ„œ λΆˆλ³€ 객체λ₯Ό μ΄μš©ν•΄ κ°œλ°œν•΄μ•Ό ν•˜λŠ” μ΄μœ λ‘œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • λΆˆλ³€ κ°μ²΄λŠ” Thread-Safe ν•˜μ—¬ 동기화λ₯Ό κ³ λ €ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€
  • 만일 κ°€λ³€ 객체λ₯Ό 톡해 μž‘μ—…μ„ ν•˜λŠ” 도쀑 μ˜ˆμ™Έ(Exception)κ°€ λ°œμƒν•˜λ©΄ ν•΄λ‹Ή 객체가 λΆˆμ•ˆμ •ν•œ μƒνƒœμ— 빠질 수 μžˆμ–΄ 또 λ‹€λ₯Έ μ—λŸ¬λ₯Ό μœ λ°œν•  수 μžˆλŠ” μœ„ν—˜μ„±μ΄ 있기 λ•Œλ¬Έμ΄λ‹€.
  • λΆˆλ³€ 객체둜 κ΅¬μ„±ν•˜λ©΄ λ‹€λ₯Έ μ‚¬λžŒμ΄ κ°œλ°œν•œ ν•¨μˆ˜λ₯Ό μœ„ν—˜μ—†μ΄ μ΄μš©μ„ 보μž₯ν•  수 μžˆμ–΄ ν˜‘μ—…μ—λ„ μœ μ§€λ³΄μˆ˜μ—λ„ μœ μš©ν•˜λ‹€.

λ”°λΌμ„œ ν΄λž˜μŠ€λ“€μ€ 가변적 이여야 ν•˜λŠ” 맀우 νƒ€λ‹Ήν•œ μ΄μœ κ°€ μžˆμ§€ μ•ŠλŠ” ν•œ λ°˜λ“œμ‹œ λΆˆλ³€μœΌλ‘œ λ§Œλ“€μ–΄μ•Ό ν•œλ‹€. λ§Œμ•½ 클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“œλŠ” 것이 λΆˆκ°€λŠ₯ν•˜λ‹€λ©΄ κ°€λŠ₯ν•œ λ³€κ²½ κ°€λŠ₯성을 μ΅œμ†Œν™”ν•΄μ•Ό ν•œλ‹€.

예λ₯Όλ“€μ–΄ κ²½μš°μ— 따라 λ³€μˆ˜μ— final ν‚€μ›Œλ“œλ₯Ό λΆ™μΌμˆ˜ μ—†λŠ” 상황이 생길 μˆ˜λ„ μžˆλ‹€. μ΄λ•ŒλŠ” Setter λ©”μ„œλ“œ 자체λ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•ŠμŒμœΌλ‘œμ„œ λΆˆλ³€ 객체λ₯Ό κ°„μ ‘μ μœΌλ‘œ ꡬ성이 κ°€λŠ₯ν•˜λ‹€.그러면 결ꡭ은 λŒλ„ λŒμ•„ μƒμ„±μžλ₯Ό μ΄μš©ν•˜λΌλŠ” 것인데 μ—­μ‹œλ‚˜ μ§€λ‚˜μΉœ μƒμ„±μž μ˜€λ²„λ‘œλ”© λ¬Έμ œκ°€ λ°œμƒν•˜κ²Œ λœλ‹€. κ·Έλž˜μ„œ μ—°κ΅¬λœκ²ƒμ΄ λΉŒλ” ν΄λž˜μŠ€μ΄λ‹€.

 

즉, μ΅œμ’… μ •λ¦¬ν•˜μžλ©΄ λΉŒλ” νŒ¨ν„΄μ€ μƒμ„±μž 없이 μ–΄λŠ 객체에 λŒ€ν•΄ 'λ³€κ²½ κ°€λŠ₯성을 μ΅œμ†Œν™”' λ₯Ό μΆ”κ΅¬ν•˜μ—¬ λΆˆλ³€μ„±μ„ κ°–κ²Œ ν•΄μ£Όκ²Œ λ˜λŠ” 것이닀.


λΉŒλ” νŒ¨ν„΄ λ‹¨μ 

 

1. μ½”λ“œ λ³΅μž‘μ„± 증가

μš°μ„  λΉŒλ” νŒ¨ν„΄μ„ μ μš©ν•˜λ €λ©΄ N개의 ν΄λž˜μŠ€μ— λŒ€ν•΄ N개의 μƒˆλ‘œμš΄ λΉŒλ” 클래슀λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•΄μ„œ, 클래수 μˆ˜κ°€ κΈ°ν•˜κΈ‰μˆ˜μ μœΌλ‘œ λŠ˜μ–΄λ‚˜ κ΄€λ¦¬ν•΄μ•Ό ν•  ν΄λž˜μŠ€κ°€ λ§Žμ•„μ§€κ³  ꡬ쑰가 λ³΅μž‘ν•΄μ§ˆ 수 μžˆλ‹€. λ˜ν•œ 선택적 λ§€κ°œλ³€μˆ˜λ₯Ό 많이 λ°›λŠ” 객체λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ¨Όμ € λΉŒλ” ν΄λž˜μŠ€λΆ€ν„° μ •μ˜ν•΄μ•Όν•œλ‹€. λ‹€λ§Œ 이뢀뢄은 μ—¬λŠ λ””μžμΈ νŒ¨ν„΄μ΄ κ°€μ§€λŠ” 단점이기도 ν•˜λ‹€.

 

2. μƒμ„±μž λ³΄λ‹€λŠ” μ„±λŠ₯은 떨어진닀.

맀번 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ λΉŒλ”λ₯Ό 거쳐 μΈμŠ€ν„΄μŠ€ν™” ν•˜κΈ° λ•Œλ¬Έμ— μ–΄μ©Œλ©΄ λ‹Ήμ—°ν•œ 말일지도 λͺ¨λ₯Έλ‹€. 비둝 생성 λΉ„μš© μžμ²΄λŠ” ν¬μ§€λŠ” μ•Šμ§€λ§Œ, μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ„±λŠ₯을 극으둜 μ€‘μš”μ‹œλ˜λŠ” 상황이라면 λ¬Έμ œκ°€ 될수 μžˆλ‹€.

 

3. μ§€λ‚˜μΉœ λΉŒλ” λ‚¨μš©μ€ κΈˆμ§€

클래슀의 ν•„λ“œμ˜ κ°œμˆ˜κ°€ 4개 보닀 적고, ν•„λ“œμ˜ λ³€κ²½ κ°€λŠ₯성이 μ—†λŠ” 경우라면 차라리 μƒμ„±μžλ‚˜ 정적 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•˜λŠ” 것이 더 쒋을 수 μžˆλ‹€. λΉŒλ” νŒ¨ν„΄μ˜ μ½”λ“œκ°€ λ‹€μ†Œ μž₯ν™©ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ 클래슀 ν•„λ“œμ˜ κ°―μˆ˜μ™€ ν•„λ“œ λ³€κ²½ κ°€λŠ₯성을 μ€‘μ μœΌλ‘œ 보고 νŒ¨ν„΄μ„ 적용 유무λ₯Ό κ°€λ €μ•Όν•œλ‹€.

λ‹€λ§Œ API λŠ” μ‹œκ°„μ΄ μ§€λ‚ μˆ˜λ‘ λ§Žμ€ λ§€κ°œλ³€μˆ˜λ₯Ό κ°–λŠ” κ²½ν–₯이 있기 λ•Œλ¬Έμ— μ• μ΄ˆμ— λΉŒλ” νŒ¨ν„΄μœΌλ‘œ μ‹œμž‘ν•˜λŠ” 편이 λ‚˜μ„ λ•Œκ°€ λ§Žλ‹€κ³  λ§ν•˜λŠ” κ²½ν–₯도 μžˆλ‹€.


Builder λ””μžμΈ νŒ¨ν„΄ μ’…λ₯˜

λΉŒλ” νŒ¨ν„΄μ—λŠ” 여타 λ””μžμΈ νŒ¨ν„΄κ³ΌλŠ” λ‹€λ₯΄κ²Œ 두가지 λ””μžμΈ μ’…λ₯˜κ°€ μ‘΄μž¬ν•œλ‹€. GOF의 λ””μžμΈ νŒ¨ν„΄μ—μ„œ μ†Œκ°œν•˜λŠ” λΉŒλ” νŒ¨ν„΄κ³Ό μ΄νŽ™ν‹°λΈŒ μžλ°”(Effective Java) μ±…μ—μ„œ μ†Œκ°œν•˜λŠ” λΉŒλ” νŒ¨ν„΄ ꡬ쑰가 μ„œλ‘œ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ΄λ‹€. 

  1. μ΄νŽ™ν‹°λΈŒ μžλ°”μ˜ λΉŒλ” νŒ¨ν„΄ : μƒμ„±μ‹œ 지정해야 ν•  μΈμžκ°€ λ§Žμ„λ•Œ μ‚¬μš©. 객체의 일관성 λΆˆλ³€μ„±μ΄ λͺ©μ .
  2. GoF의 λΉŒλ” νŒ¨ν„΄ : 객체의 생성 단계 μˆœμ„œλ₯Ό 결정해두고 각 단계λ₯Ό λ‹€μ–‘ν•˜κ²Œ κ΅¬ν˜„ν•˜κ³  μ‹Άμ„λ•Œ μ‚¬μš©.

일단 두 μ±…μ˜ 관점이 λ‹€λ₯΄κ³  GoF의 λ””μžμΈ νŒ¨ν„΄μ€ 1994년에 λ°œλ§€λ˜μ—ˆκ³  μ΄νŽ™ν‹°λΈŒ μžλ°”κ°€ 2001년에 λ‚˜μ€‘μ— λ‚˜μ™”κΈ° λ•Œλ¬Έμ΄λΌκ³  생각 λœλ‹€.


μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄ (Effective Java)

보톡 κ°œλ°œμžλ“€μ΄ λΉŒλ” νŒ¨ν„΄μ„ 말할 λ•Œ μ •μ˜λ˜λŠ” 것이 μ΄νŽ™ν‹°λΈŒ μžλ°”μ—μ„œ μ†Œκ°œν•œ λΉŒλ” νŒ¨ν„΄μ΄λ‹€. GOF λΉŒλ” νŒ¨ν„΄κ³Ό κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄ μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄(Simple Builder Pattern) 이라고도 λΆˆλ¦¬μš΄λ‹€.

μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄μ€ μƒμ„±μžκ°€ λ§Žμ„ 경우 λ˜λŠ” λ³€κ²½ λΆˆκ°€λŠ₯ν•œ λΆˆλ³€ 객체가 ν•„μš”ν•œ 경우 μ½”λ“œμ˜ 가독성과 일관성, λΆˆλ³€μ„±μ„ μœ μ§€ν•˜λŠ” 것에 쀑점을 λ‘”λ‹€. μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄μ€ μœ„μ—μ„œ μš°λ¦¬κ°€ 배운 λΉŒλ” νŒ¨ν„΄κ³Ό 차이가 거의 μ—†λ‹€. λ‹€λ§Œ λΉŒλ”(Builder) ν΄λž˜μŠ€κ°€ κ΅¬ν˜„ν•  클래슀의 정적 λ‚΄λΆ€ 클래슀(Static Inner Class)둜 κ΅¬ν˜„λœλ‹€λŠ” 점이 λ‹€λ₯΄λ‹€.

simple-builder-pattern

λΉŒλ” ν΄λž˜μŠ€κ°€ static inner class둜 κ΅¬ν˜„λ˜λŠ” μ΄μœ λ‘œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

 

첫 번째, ν•˜λ‚˜μ˜ λΉŒλ” ν΄λž˜μŠ€λŠ” ν•˜λ‚˜μ˜ λŒ€μƒ 객체 μƒμ„±λ§Œμ„ μœ„ν•΄ μ‚¬μš©λœλ‹€. κ·Έλž˜μ„œ 두 클래슀λ₯Ό 물리적으둜 κ·Έλ£Ήν•‘ν•¨μœΌλ‘œμ¨ 두 ν΄λž˜μŠ€κ°„μ˜ 관계에 λŒ€ν•œ νŒŒμ•…μ„ μ‰½κ²Œ ν•  수 μžˆλ‹€.

두 번째, λŒ€μƒ κ°μ²΄λŠ” μ˜€λ‘œμ§€ λΉŒλ” 객체에 μ˜ν•΄ μ΄ˆκΈ°ν™” λœλ‹€. 즉, μƒμ„±μžλ₯Ό 외뢀에 λ…ΈμΆœμ‹œν‚€λ©΄ μ•ˆλ˜κΈ° λ•Œλ¬Έμ— μƒμ„±μžλ₯Ό private둜 ν•˜κ³ , λ‚΄λΆ€ λΉŒλ” ν΄λž˜μŠ€μ—μ„œ private μƒμ„±μžλ₯Ό ν˜ΈμΆœν•¨μœΌλ‘œμ¨ μ˜€λ‘œμ§€ λΉŒλ” 객체에 μ˜ν•΄ μ΄ˆκΈ°ν™” λ˜λ„λ‘ 섀계 ν•  수 μžˆλ‹€.

μ„Έ 번째, inner classλ₯Ό μ“°λ©΄ 쒋은건 μ•Œκ² λŠ”λ° μ™œ ν•˜ν•„ static 으둜 μ„ μ–Έν•΄μ£Όμ–΄μ•Ό ν•˜λƒλ©΄, 정적 λ‚΄λΆ€ ν΄λž˜μŠ€λŠ” μ™ΈλΆ€ 클래슀의 μΈμŠ€ν„΄μŠ€ 없이도 생성할 수 μžˆλŠ”λ°, 만일 일반 λ‚΄λΆ€ 클래슀둜 κ΅¬μ„±ν•œλ‹€λ©΄ λ‚΄λΆ€ 클래슀λ₯Ό μƒμ„±ν•˜κΈ°λ„ 전에 μ™ΈλΆ€ 클래슀λ₯Ό μΈμŠ€ν„΄μŠ€ν™” ν•΄μ•Ό ν•œλ‹€. λΉŒλ”κ°€ μ΅œμ’…μ μœΌλ‘œ 생성할 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό λ¨Όμ € 생성해야 ν•œλ‹€λ©΄ λͺ¨μˆœμ΄ 생기기 λ•Œλ¬Έμ΄λ‹€.

λ„€ 번째, λ©”λͺ¨λ¦¬ λˆ„μˆ˜ 문제 λ•Œλ¬Έμ— static으둜 λ‚΄λΆ€ 클래슀λ₯Ό μ •μ˜ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€. μžμ„Έν•œκ±΄ λ‹€μŒ ν¬μŠ€νŒ…μ„ μ°Έκ³ ν•˜κΈΈ λ°”λž€λ‹€.

 

Simple λΉŒλ” νŒ¨ν„΄ κ΅¬ν˜„ν•˜κΈ°

simple-builder-pattern

  1. λΉŒλ” 클래슀λ₯Ό Static Nested Class둜 μ •μ˜ν•œλ‹€.
  2. λΉŒλ”λ₯Ό 톡해 μΈμŠ€ν„΄μŠ€ν™” ν•˜κΈ° λ•Œλ¬Έμ— λŒ€μƒ 객체 μƒμ„±μžλŠ” private둜 μ •μ˜ν•œλ‹€.
  3. λΉŒλ” 클래슀의 μƒμ„±μžλŠ” public으둜 ν•˜λ©°, ν•„μˆ˜ νŒŒλΌλ―Έν„°μ— λŒ€ν•΄ μƒμ„±μžμ˜ νŒŒλΌλ―Έν„°λ‘œ λ°›λŠ”λ‹€.
  4. 선택적 νŒŒλΌλ―Έν„°μ— λŒ€ν•΄μ„œλŠ” λ©”μ†Œλ“œλ‘œ μ œκ³΅ν•œλ‹€. μ΄λ•Œ λ©”μ†Œλ“œμ˜ λ°˜ν™˜κ°’μ€ λΉŒλ” 객체 μžμ‹ (this) 이어야 ν•œλ‹€.
  5. λ§ˆμ§€λ§‰ λ‹¨κ³„λ‘œ μ΅œμ’… 객체λ₯Ό μƒμ„±ν•˜λŠ” build() λ©”μ†Œλ“œλ₯Ό μ •μ˜ν•˜μ—¬ ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ΅œμ’… μƒμ„±λœ 결과물을 μ œκ³΅ν•œλ‹€.
  6. μ΄λ•Œ μƒμ„±μžμ˜ 인수둜 λΉŒλ” μΈμŠ€ν„΄μŠ€ μžκΈ°μžμ‹ μ„ μ „λ‹¬ν•˜κ³ , λŒ€μƒ 객체 μƒμ„±μžμ—μ„œ λΉŒλ” μΈμŠ€ν„΄μŠ€μ˜ ν•„λ“œλ₯Ό 각각 λŒ€μž…ν•˜μ—¬ μ΅œμ’… 완성본이 λ‚˜μ˜€κ²Œ λœλ‹€.
class Person {
    // final ν‚€μ›Œλ“œλ‘œ ν•„λ“œλ“€μ„ λΆˆλ³€ 객체둜 λ§Œλ“ λ‹€.
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;

    // 정적 λ‚΄λΆ€ λΉŒλ” 클래슀
    public static class Builder {

        // ν•„μˆ˜ νŒŒλΌλ―Έν„°
        private final String name;
        private final String age;

        // 선택 νŒŒλΌλ―Έν„°
        private String gender;
        private String job;
        private String birthday;
        private String address;

        // ν•„μˆ˜ νŒŒλΌλ―Έν„°λŠ” λΉŒλ” μƒμ„±μžλ‘œ λ°›κ²Œ ν•œλ‹€
        public Builder(String name, String age) {
            this.name = name;
            this.age = age;
        }

        // 선택 νŒŒλΌλ―Έν„°λŠ” 각 λ©”μ„œλ“œλ₯Ό 톡해 μ •μ˜ν•œλ‹€
        public Builder gender(String gender) {
            this.gender = gender;
            return this;
        }

        public Builder job(String job) {
            this.job = job;
            return this;
        }

        public Builder birthday(String birthday) {
            this.birthday = birthday;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        // λŒ€μƒ 객체의 private μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜μ—¬ μ΅œμ’… μΈμŠ€ν„΄μŠ€ν™” ν•œλ‹€
        public Person build() {
            return new Person(this); // λΉŒλ” 객체 μžμ‹ μ„ λ„˜κΈ΄λ‹€.
        }
    }

    // private μƒμ„±μž - μƒμ„±μžλŠ” μ™ΈλΆ€μ—μ„œ ν˜ΈμΆœλ˜λŠ”κ²ƒμ΄ μ•„λ‹Œ λΉŒλ” ν΄λž˜μŠ€μ—μ„œλ§Œ 호좜되기 λ•Œλ¬Έμ—
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
        this.job = builder.gender;
        this.birthday = builder.birthday;
        this.address = builder.address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", gender='" + gender + '\'' +
                ", job='" + job + '\'' +
                ", birthday='" + birthday + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
public static void main(String[] args) {

    Person person = new Person
            .Builder("홍길동", "26") // static inner class μ΄ˆκΈ°ν™” (ν•„μˆ˜ νŒŒλΌλ―Έν„°)
            .gender("man") // 선택 νŒŒλΌλ―Έν„°
            .job("Warrior")
            .birthday("1800.10.10")
            .address("μ‘°μ„ ")
            .build();

    System.out.println(person);
}

simple-builder-pattern


λ””λ ‰ν„° λΉŒλ” νŒ¨ν„΄ (GOF)

GOFμ—μ„œ μ •μ˜ν•˜κ³  μžˆλŠ” λ””μžμΈ νŒ¨ν„΄μ€ λ³΅μž‘ν•œ κ°μ²΄μ˜ 생성 μ•Œκ³ λ¦¬μ¦˜κ³Ό 쑰립 방법을 λΆ„λ¦¬ν•˜μ—¬ λΉŒλ“œ 곡정을 κ΅¬μΆ•ν•˜λŠ”κ²ƒμ΄ λͺ©μ μ΄λ‹€. λΉŒλ”λ₯Ό λ°›μ•„ 쑰립 방법을 μ •μ˜ν•œ 클래슀λ₯Ό Director라고 λΆ€λ₯Έλ‹€.

μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄μ€ ν•˜λ‚˜μ˜ λŒ€μƒ 객체에 λŒ€ν•œ μƒμ„±λ§Œμ„ λͺ©μ μ„ λ‘μ§€λ§Œ, λ””λ ‰ν„° λΉŒλ” νŒ¨ν„΄μ€ μ—¬λŸ¬κ°€μ§€μ˜ λΉŒλ“œ ν˜•μ‹μ„ μœ μ—°ν•˜κ²Œ μ²˜λ¦¬ν•˜λŠ” 것에 λͺ©μ μ„ λ‘”λ‹€. μ–΄μ°Œλ³΄λ©΄ 일반적인 λΉŒλ” νŒ¨ν„΄μ„ 고도화 μ‹œν‚¨ νŒ¨ν„΄μ΄λΌκ³  λ³Ό 수 도 μžˆλ‹€.

 

Director λΉŒλ” νŒ¨ν„΄ ꡬ쑰

gof-builder

  • Builder : λΉŒλ” 좔상 클래슀
  • ConcreteBuilder : Builder의 κ΅¬ν˜„μ²΄. Product 생성을 λ‹΄λ‹Ήν•œλ‹€.
  • Director : Builderμ—μ„œ μ œκ³΅ν•˜λŠ” λ©”μ„œλ“œλ“€μ„ μ‚¬μš©ν•΄ 정해진 μˆœμ„œλŒ€λ‘œ Product μƒμ„±ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό μ •μ˜
  • Product : Directorκ°€ Builder둜 λ§Œλ“€μ–΄λ‚Έ κ²°κ³Όλ¬Ό.

μ΄λŸ¬ν•œ κ΅¬μ‘°λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ 직접 λΉŒλ”μ˜ λͺ¨λ“  APIλ₯Ό μ‚¬μš©ν•˜λŠ” 게 μ•„λ‹Œ, Director을 ν†΅ν•΄μ„œ κ°„λ‹¨ν•˜κ²Œ μΈμŠ€ν„΄μŠ€λ₯Ό μ–»μ–΄μ˜¬ 수 있고 μ½”λ“œλ₯Ό μž¬μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

 

Director λΉŒλ” νŒ¨ν„΄ κ΅¬ν˜„ν•˜κΈ°

λ‹€μŒ μ˜ˆμ œλŠ” 일반적인 μžλ°” 데이터λ₯Ό μ €μž₯ν•˜κ³  μžˆλŠ” Data 객체λ₯Ό Builder μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 μ μ ˆν•œ λ¬Έμžμ—΄ 포맷으둜 λ³€ν™˜ν•˜λŠ” μ˜ˆμ œμ΄λ‹€.

  • PlainTextBuilder : Data μΈμŠ€ν„΄μŠ€μ˜ 데이터듀을 ν‰μ΄ν•œ ν…μŠ€νŠΈ ν˜•νƒœλ‘œ λ§Œλ“œλŠ” API
  • JSONBuilder : Data μΈμŠ€ν„΄μŠ€μ˜ 데이터듀을 JSON ν˜•νƒœλ‘œ λ§Œλ“œλŠ” API
  • XMLBuilder : Data μΈμŠ€ν„΄μŠ€μ˜ 데이터듀을 XML ν˜•νƒœλ‘œ λ§Œλ“œλŠ” API

μ΄λ•Œ 각 λ¬Έμžμ—΄ 포맷으둜 λ³€ν™˜ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό Director 객체가 λ‹΄λ‹Ήν•˜κ²Œ λœλ‹€. ν΄λΌμ΄μ–ΈνŠΈλŠ” Directorλ₯Ό μ΄μš©ν•˜μ—¬ κ°„λ‹¨ν•œ λ©”μ„œλ“œ 호좜만으둜 λ³΅μž‘ν•œ λΉŒλ“œ 곡정 과정을 μ•Œν•„μš”μ—†μ΄ 결과물을 얻을 수 μžˆλ‹€.

gof-builder

class Data {
    private String name;
    private int age;

    public Data(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
abstract class Builder {
    // μƒμ†ν•œ μžμ‹ ν΄λž˜μŠ€μ—μ„œ μ‚¬μš©ν•˜λ„λ‘ protected μ ‘κ·Όμ œμ–΄μž 지정
    protected Data data;

    public Builder(Data data) {
        this.data = data;
    }

    // Data 객체의 데이터듀을 μ›ν•˜λŠ” ν˜•νƒœμ˜ λ¬Έμžμ—΄ 포맷을 ν•΄μ£ΌλŠ” λ©”μ„œλ“œλ“€ (머리 - 쀑간 - 끝 ν˜•μ‹)
    public abstract String head();
    public abstract String body();
    public abstract String foot();

}
// Data 데이터듀을 ν‰λ²”ν•œ λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” λΉŒλ”
class PlainTextBuilder extends Builder {

    public PlainTextBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        return "";
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append("Name: ");
        sb.append(data.getName());
        sb.append(", Age: ");
        sb.append(data.getAge());

        return sb.toString();
    }

    @Override
    public String foot() {
        return "";
    }
}
// Data 데이터듀을 JSON ν˜•νƒœμ˜ λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” λΉŒλ”
class JSONBuilder extends Builder {

    public JSONBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        return "{\n";
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append("\t\"Name\" : ");
        sb.append("\"" + data.getName() + "\",\n");
        sb.append("\t\"Age\" : ");
        sb.append(data.getAge());

        return sb.toString();
    }

    @Override
    public String foot() {
        return "\n}";
    }
}
// Data 데이터듀을 XML ν˜•νƒœμ˜ λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” λΉŒλ”
class XMLBuilder extends Builder {
    public XMLBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        StringBuilder sb = new StringBuilder();

        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        sb.append("<DATA>\n");

        return sb.toString();
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append("\t<NAME>");
        sb.append(data.getName());
        sb.append("<NAME>");
        sb.append("\n\t<AGE>");
        sb.append(data.getAge());
        sb.append("<AGE>");

        return sb.toString();
    }

    @Override
    public String foot() {
        return "\n</DATA>";
    }
}
// 각 λ¬Έμžμ—΄ 포맷 λΉŒλ“œ 과정을 ν…œν”Œλ¦Ών™” μ‹œν‚¨ λ””λ ‰ν„°
class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    // μΌμ’…μ˜ λΉŒλ“œ ν…œν”Œλ¦Ώ λ©”μ„œλ“œλΌ 보면 λœλ‹€
    public String build() {
        StringBuilder sb = new StringBuilder();

		// λΉŒλ” κ΅¬ν˜„μ²΄μ—μ„œ μ •μ˜ν•œ 생성 μ•Œκ³ λ¦¬μ¦˜μ΄ 싀행됨
        sb.append(builder.head());
        sb.append(builder.body());
        sb.append(builder.foot());

        return sb.toString();
    }
}
public static void main(String[] args) {
    // 1. 포맷할 μžλ°” 데이터 생성
    Data data = new Data("홍길동", 44);

    // 2. 일반 ν…μŠ€νŠΈλ‘œ ν¬λ§·ν•˜μ—¬ 좜λ ₯ν•˜κΈ°
    Builder builder1 = new PlainTextBuilder(data);
    Director director1 = new Director(builder1);
    String result1 = director1.build();
    System.out.println(result1);

    // 3. JSON ν˜•μ‹μœΌλ‘œ ν¬λ§·ν•˜μ—¬ 좜λ ₯ν•˜κΈ°
    Builder builder2 = new JSONBuilder(data);
    Director director2 = new Director(builder2);
    String result2 = director2.build();
    System.out.println(result2);

    // 4. XML ν˜•μ‹μœΌλ‘œ ν¬λ§·ν•˜μ—¬ 좜λ ₯ν•˜κΈ°
    Builder builder3 = new XMLBuilder(data);
    Director director3 = new Director(builder3);
    String result3 = director3.build();
    System.out.println(result3);
}

gof-builder

이처럼 DirectorλŠ” ν…œν”Œλ¦Ών™” ν•œ λ©”μ„œλ“œλ₯Ό 톡해 μΌκ΄€λœ ν”„λ‘œμ„ΈμŠ€λ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” λΉŒλ“œ 과정을 λ‹¨μˆœν™” ν•˜κ³ , ν΄λΌμ΄μ–ΈνŠΈ μͺ½μ—μ„  Directorκ°€ μ œκ³΅ν•˜λŠ” λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ―€λ‘œμ¨ μ½”λ“œλ₯Ό μž¬μ‚¬μš©ν•  수 있게 λœλ‹€.

즉, Builder λŠ” λΆ€ν’ˆμ„ λ§Œλ“€κ³ , DirectorλŠ” γ… uilderκ°€ λ§Œλ“  λΆ€ν’ˆμ„ μ‘°ν•©ν•΄ μ œν’ˆμ„ λ§Œλ“ λ‹€κ³  ν•  수 μžˆλ‹€.

 

GOF의 λΉŒλ” νŒ¨ν„΄μ€ μ—¬λŸ¬ λ””μžμΈ νŒ¨ν„΄μ˜ 짬뽕

μ§€κΈˆ κΉŒμ§€ κ΅¬ν˜„ν•΄λ³Έ GOF의 λ””λ ‰ν„° λΉŒλ” νŒ¨ν„΄μ€ μ–΄μ°Œ 보면 λΉŒλ“œ 과정을 좔상화 및 λ‹¨μˆœν™”ν•œ Facade νŒ¨ν„΄κ³Ό λΉŒλ“œ κ³Όμ • μ½”λ“œλ₯Ό ν…œν”Œλ¦Ών™”ν•œ Template Method νŒ¨ν„΄ 그리고 μ›ν•˜λŠ” λ¬Έμžμ—΄ ν˜•μ‹μ˜ 각 λΉŒλ“œ μ „λž΅ μ•Œκ³ λ¦¬μ¦˜μ„ μ •μ˜ν•œ Strage νŒ¨ν„΄μ„ 짬뽕 μ‹œν‚¨ λ””μžμΈ νŒ¨ν„΄μ΄λΌκ³  λ³Όμˆ˜λ„ μžˆλ‹€.

gof-builder


Lombok의 @Builder

κ°œλ°œμžκ°€ 쒀더 νŽΈν•˜κ²Œ λΉŒλ” νŒ¨ν„΄μ„ μ΄μš©ν•˜κΈ° μœ„ν—€ Lombokμ—μ„œλŠ” λ³„λ„μ˜ μ–΄λ…Έν…Œμ΄μ…˜μ„ μ§€μ›ν•œλ‹€. ν΄λž˜μŠ€μ— @Builder μ–΄λ…Έν…Œμ΄μ…˜λ§Œ λΆ™μ—¬μ£Όλ©΄ 클래슀λ₯Ό 컴파일 ν•  λ•Œ μžλ™μœΌλ‘œ 클래슀 내뢀에 λΉŒλ” APIκ°€ λ§Œλ“€μ–΄μ§„λ‹€.

단, 둬볡의 @BuilderλŠ” GOF의 λ””λ ‰ν„° λΉŒλ”κ°€ μ•„λ‹Œ μ‹¬ν”Œ λΉŒλ” νŒ¨ν„΄μ„ λ‹€λ£¬λ‹€λŠ” 점은 μœ μ˜ν•˜μž.

 

λΉŒλ” μ–΄λ…Έν…Œμ΄μ…˜ κ΅¬ν˜„ν•˜κΈ°

  • @Builder : PersonBuilder λΉŒλ” ν΄λž˜μŠ€μ™€ 이λ₯Ό λ°˜ν™˜ν•˜λŠ” builder() λ©”μ„œλ“œ 생성
  • @AllArgsConstructor(access = AccessLevel.PRIVATE) : @Builder μ–΄λ…Έν…Œμ΄μ…˜μ„ μ„ μ–Έν•˜λ©΄ 전체 인자λ₯Ό κ°–λŠ” μƒμ„±μžλ₯Ό μžλ™μœΌλ‘œ λ§Œλ“œλŠ”λ°, 이λ₯Ό private μƒμ„±μžλ‘œ μ„€μ •
  • @ToString : toString() λ©”μ„œλ“œ μžλ™ 생성
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.ToString;

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
class Person {
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;
}
public static void main(String[] args) {
    Person person = Person.builder()
            .name("홍길동")
            .age("26")
            .gender("man") // 선택 νŒŒλΌλ―Έν„°
            .job("Warrior")
            .birthday("1800.10.10")
            .address("μ‘°μ„ ")
            .build();
}

 

ν•„μˆ˜ νŒŒλΌλ―Έν„° λΉŒλ” κ΅¬ν˜„ν•˜κΈ°

@Builder μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ λΉŒλ” νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜λ©΄ ν•„μˆ˜ νŒŒλΌλ―Έν„° μ μš©μ„ μ§€μ •ν•΄μ€„μˆ˜κ°€ μ—†λ‹€. λ”°λΌμ„œ λŒ€μƒ 객체 μ•ˆμ— λ³„λ„μ˜ builder() 정적 λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•¨μœΌλ‘œμ¨, λΉŒλ” 객체λ₯Ό μƒμ„±ν•˜κΈ° 전에 ν•„μˆ˜ νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •ν•˜λ„λ‘ μœ λ„ν• μˆ˜ 있고, λ˜ν•œ νŒŒλΌλ―Έν„° 검증 λ‘œμ§λ„ 좔가해쀄 수 μžˆλ‹€.

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
class Person {
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;

    // ν•„μˆ˜ νŒŒλΌλ―Έν„° λΉŒλ” λ©”μ„œλ“œ κ΅¬ν˜„
    public static PersonBuilder builder(String name, String age) {
        // λΉŒλ”μ˜ νŒŒλΌλ―Έν„° 검증
        if(name == null || age == null)
            throw new IllegalArgumentException("ν•„μˆ˜ νŒŒλΌλ―Έν„° λˆ„λ½");

        // ν•„μˆ˜ νŒŒλΌλ―Έν„°λ₯Ό 미리 λΉŒλ“œν•œ λΉŒλ” 객체λ₯Ό λ°˜ν™˜ (지연 λΉŒλ” 원리)
        return new PersonBuilder().name(name).age(age);
    }
}
public static void main(String[] args) {

    Person person = Person.builder("홍길동", "26") // ν•„μˆ˜ νŒŒλΌλ―Έν„°
            .gender("man") // 선택 νŒŒλΌλ―Έν„°
            .job("Warrior")
            .birthday("1800.10.10")
            .address("μ‘°μ„ ")
            .build();

    System.out.println(person);
}

μ‹€λ¬΄μ—μ„œ μ°Ύμ•„λ³΄λŠ” Builder νŒ¨ν„΄

 

Java

  • java.lang.StringBuilder의 append()
  • java.lang.StringBuffer의 append()
  • java.nio.ByteBuffer의 put() - CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer 도 λ§ˆμ°¬κ°€μ§€
  • javax.swing.GroupLayout.Group의 addComponent()
  • java.lang.Appendable의 κ΅¬ν˜„μ²΄
  • java.util.stream.Stream.Builder

 

StringBuilder

μžλ°”μ˜ λ¬Έμžμ—΄ ν΄λž˜μŠ€ν•˜λ©΄ λ°°μš°λŠ” StringBuilderκ°€ μ΄λ¦„μ—μ„œ 보듯이 κ·Έ μ˜ˆμ΄λ‹€.

λΉŒλ”μ— ν•΄λ‹Ήν•˜λŠ” StringBuilderλ₯Ό μƒμ„±ν•˜κ³ , λΉŒλ”κ°€ μ œκ³΅ν•˜λŠ” append λ©”μ„œλ“œλ‘œ νŒŒλΌλ―Έν„°λ₯Ό κ΅¬μ„±ν•˜κ³ , μ΅œμ’…μ μœΌλ‘œ toString을 ν˜ΈμΆœν•΄μ„œ String 객체λ₯Ό μƒμ„±ν•˜λŠ” 일련의 과정이 λΉŒλ” νŒ¨ν„΄μ΄κΈ° λ•Œλ¬Έμ΄λ‹€.

public static void main(String[] args) {
    String result = new StringBuilder()
            .append("hello ")
            .append("world!")
            .toString(); // build()

    System.out.println(result);
}

 

StreamBuilder

Stream APIκ°€ μ œκ³΅ν•˜λŠ” StreamBuilder도 λ§ˆμ°¬κ°€μ§€μ΄λ‹€. StreamBuilderλ₯Ό μ‚¬μš©ν•˜λ©΄ Stream에 λ“€μ–΄κ°ˆ μš”μ†Œλ₯Ό add ν•  수 있고, μ΅œμ’…μ μœΌλ‘œ buildλ₯Ό ν˜ΈμΆœν•΄μ„œ stream 객체λ₯Ό μƒμ„±ν•œλ‹€.

public static void main(String[] args) {
    Stream.Builder<String> stringBuilder = Stream.builder();
    Stream<String> stream = stringBuilder.add("hello").add("world!").add("bye..").build();

    stream.forEach(System.out::println);
}

Spring Framework

μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ—μ„  UriComponents μΈμŠ€ν„΄μŠ€λ₯Ό Uricomponentsbuilderλ₯Ό ν†΅ν•΄μ„œ λ§Œλ“€ 수 μžˆλ‹€. uriλ₯Ό ν•˜λ“œμ½”λ”©ν•΄μ„œ λ§Œλ“œλŠ” 것보닀 μ•ˆμ „ν•˜κ²Œ λ§Œλ“€ 수 μžˆλ‹€.

public static void main(String[] args) {
    UriComponents uriComponents = UriComponentsBuilder.newInstance()
            .scheme("https")
            .host("kangworld.tistory.com")
            .build();

    System.out.println(uriComponents);
}

# 참고자료

μ΄νŽ™ν‹°λΈŒ μžλ°” Effective Java 3/E

μ½”λ”©μœΌλ‘œ ν•™μŠ΅ν•˜λŠ” GoF의 λ””μžμΈ νŒ¨ν„΄ - λ°±κΈ°μ„ 

https://refactoring.guru/design-patterns/builder

https://www.youtube.com/watch?v=_GCiJAFU2DU