빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여, 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 생성 디자인 패턴입니다. 매개변수가 많은 객체를 만들 때 생성자(Constructor) 오버로딩이 끝도 없이 늘어나는 '점층적 생성자 패턴(Telescoping Constructor Pattern)'의 문제를 해결하기 위해 고안되었습니다.
1. 빌더 패턴의 구조 (Inner Builder UML)
자바(Java) 진영에서 가장 널리 쓰이는 Effective Java 스타일의 정적 내부 클래스(Static Inner Class) 빌더 패턴 구조입니다. 체이닝(Chaining) 기법을 통해 객체를 직관적이고 안전하게 조립할 수 있습니다.
2. 왜 빌더 패턴을 사용하나요?
장점 (Pros)
단점 및 한계 (Cons)
1. 가독성(Readability) 극대화: 매개변수 위치에 얽매이지 않고 메서드 이름으로 명확하게 어떤 값을 세팅하는지 알 수 있습니다. (메서드 체이닝 기법)
2. 불변 객체(Immutable Object) 생성: Setter 메서드를 열어두지 않고도 완벽하게 필드가 세팅된 불변 객체를 생성할 수 있어 스레드 안정성이 높아집니다.
1. 코드량 증가: 객체를 생성하기 위해 별도의 Builder 클래스를 만들어야 하므로, 필드가 몇 개 없는 단순한 객체라면 오버엔지니어링(Over-engineering)이 될 수 있습니다.
2. 모던 언어의 지원: Kotlin, Python, TypeScript 등 Named Arguments(이름 있는 매개변수)를 자체 지원하는 모던 언어에서는 빌더 패턴의 필요성이 크게 떨어집니다.
// 📂 Hamburger.js (JavaScript)
// 자바스크립트는 객체 리터럴이 훌륭하여 빌더의 필요성이 낮지만, 구조적 제한을 위해 명시적으로 구현할 수 있습니다.
class Hamburger {
// 3. 빌더에서 조립된 최종 데이터를 받아 불변 객체(혹은 캡슐화된 상태)로 만듦
constructor(builder) {
this.patty = builder.patty;
this.bun = builder.bun || '참깨 번';
this.hasCheese = builder.hasCheese || false;
}
showMenu() {
console.log(`🍔 주문: ${this.bun}, ${this.patty} 패티, 치즈 추가: ${this.hasCheese ? 'O' : 'X'}`);
}
}
class HamburgerBuilder {
constructor(patty) {
this.patty = patty; // 필수 항목
}
// 1. 선택 항목은 체이닝(Chaining)을 위해 return this를 반환
withBun(bun) {
this.bun = bun;
return this;
}
addCheese() {
this.hasCheese = true;
return this;
}
// 2. 최종 객체 반환
build() {
return new Hamburger(this);
}
}
// 📂 main.js (사용 예시)
const burger1 = new HamburgerBuilder("순쇠고기")
.withBun("브리오슈 번")
.addCheese()
.build();
burger1.showMenu(); // 🍔 주문: 브리오슈 번, 순쇠고기 패티, 치즈 추가: O
// 매개변수 순서 헷갈릴 일이 없고, 치즈를 뺀 버전도 쉽게 생성 가능
const burger2 = new HamburgerBuilder("새우").build();
burger2.showMenu(); // 🍔 주문: 참깨 번, 새우 패티, 치즈 추가: X