티스토리 뷰

빌더 패턴이란?

빌더 패턴은 객체의 생성 과정과 표현 방법을 분리해 다양한 구성의 인스턴스를 만드는 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고, 마지막에 통합 빌드해서 객체를 생성한다. 객체의 생성 로직을 클래스 밖으로 옮김으로써 생성자의 복잡성과 비즈니스 룰의 복잡성을 줄일 수 있다. 

 

빌더 패턴 배경

1. 점층적 생성자 패턴

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

 

이 패턴은 필수 매개변수와 선택 매개변수를 갖는 생성자를 여러 개 오버로딩하여 사용한다. 클래스에 많은 인자가 필요할 경우, 인자 수가 많아질수록 생성자의 파라미터 관리가 복잡해지며, 각 인자가 무엇을 의미하는지 혼란을 겪게 된다. 특히 필수로 알아야 하는 매개변수 외에 선택적인 매개변수에 대해서는 해당 값이 필요하지 않을 때도 인자를 넘겨야 하기 때문에 코드의 가독성과 유지보수성이 떨어진다.

 

 

2. 자바 빈즈(Java Beans) 패턴

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로 일일이 모두 정보를 입력해야 한다. 또한, 필수 정보들이 setter들이 있기 때문에 만약 setter로 필수 정보에 null로 초기화를 시켜버리면 객체의 의미가 사라질수도 있다.

 

-> 빌더 패턴의 도입: 위의 두 패턴의 단점을 해결하기 위해 빌더 패턴이 도입되었다. 빌더 패턴은 객체의 생성과 표현 방법을 분리하여, 같은 생성 과정에서 다른 표현 결과를 얻을 수 있게 한다. 이는 사용자가 객체의 필수 매개변수와 선택 매개변수를 더 명확하고 유연하게 설정할 수 있도록 하며, 객체의 일관성과 불변성을 유지할 수 있게 돕는다.

 빌더 패턴은 이러한 문제들을 해결하기 위해 별도의 Builder 클래스를 만들어 메소드를 통해 step-by-step 으로 값을 입력받은 후에 최종적으로 build() 메소드로 하나의 인스턴스를 생성하여 리턴하는 패턴이다.

 

빌더 패턴 사용 법

빌더 패턴을 이용해 Id, name만 있더라고 객체가 생성될 수 있도록 Student 클래스를 수정하고, StudentBuilder 클래스를 만들면?

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;
    }
    @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; //초기화 선택
    
    public StudentBuilder(int id, String name){
    	this.id=id;
        this.name=name;
    }

    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;
    }
    
    public Student build() {
        return new Student(id, name, grade, phoneNumber); // Student 생성자 호출
    }
}
public static void main(String[] args) {

    Student student1 = new StudentBuilder(20161234, "임꺽정")
                .build();

    System.out.println(student1);
    
    Student student2 = new StudentBuilder(20131111, "홍길동")
                .grade("Junior")
                .phoneNumber("010-1111-1111")
                .build();

    System.out.println(student2);
}

패턴 사용 시기

  • 복잡한 객체를 단계별로 생성해야 할 때
  • 객체 생성에 필요한 매개변수가 많거나 매개변수에 따라 변화가 많을 때

 

패턴 장점

  • 객체 생성 과정을 일관된 프로세스로 표현할 수 있다.
  • 디폴트 매개변수가 설정된 필드를 설정하는 메서드를 호출하지 않는 방식으로 마치 디폴트 매개변수를 생략하고 호출하는 효과를 간접적으로 구현할 수 있게 된다.
  • 초기화가 필수인 멤버는 빌더의 생성자로 받게 해 필수 멤버를 설정해주어야 빌더 객체가 생성되도록 유도하고, 선택적인 멤버는 빌더의 메서드로 받도록 해 필수 멤버와 선택 멤버를 구분하여 객체를 생성하도록 유도할 수 있다.
  • 객체 생성 단계를 지연할 수 있다.
  • 초기화 검증을 멤버별로 분리해 빌더의 각각의 멤버 설정 메서드에서 검증 과정을 분담함으로써 유지보수를 용이하게 할 수 있다.
  • Setter 메서드를 통해 클래스 멤버 초기화를 안함으로써 멤버에 대한 변경 가능성을 최소화할 수 있다.

 

패턴 단점

  • N개의 클래스에 대해 N개의 새로운 빌더 클래스를 만들어야 하므로 클래스 수가 늘어나 코드의 복잡성이 증가한다.
  • 생성자보다는 성능이 떨어진다.
  • 클래스의 필드의 개수가 4개보다 적고, 필드의 변경 가능성이 없는 경우라면 차라리 생성자나 정적 팩토리 메서드를 사용하는 것이 더 좋다.

참고

 

💠 빌더(Builder) 패턴 - 완벽 마스터하기

Builder Pattern 빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아

inpa.tistory.com