[디자인 패턴] 빌더(Builder) 패턴
❓빌더 패턴이 무엇일까?
빌더 패턴은 복합 객체 생성 과정을 별도의 독립된 클래스로 관리하는 패턴을 말합니다. 쉽게 말해 build라는 클래스 하나로 생성을 모두 관리할 수 있다는 건데요. 그렇다면 복합 객체, 복잡한 객체란 무엇을 뜻하는 것일까요?
❓복합 객체?
복합 객체는 내부적으로 다른 클래스의 객체를 포함하고 있는 것을 말합니다.
// Door 클래스 정의
class Door {
operate(action) {
console.log(`The door is ${action}.`);
}
}
// Car 클래스 정의
class Car {
constructor(model, year) {
this.model = model;
this.year = year;
this.doors = [];
}
addDoor(color) {
const newDoor = new Door();
this.doors.push(newDoor);
}
operateAllDoors(action) {
this.doors.forEach((door) => {
door.operate(action);
});
}
}
이런 식으로 다른 클래스에 의존하게 되는데요, 이 복합 객체가 많아질 수록, 추상 메서드 패턴으로는 객체를 생성하기가 어려워집니다. 기존 생성 패턴의 한계로 빌더 패턴이 탄생하게 되었다는 것이죠.
❗빌더 패턴을 알아보자
빌더 패턴이 복합 객체를 관리하는데 좋은 패턴이라는 것을 알았습니다. 그렇다면 어떤식으로 동작하는 것일까요?
빌더 패턴에서 눈에 보이는 것은, ‘빌더’와 ‘알고리즘’입니다.
빌더는 추상화를 통해 복합 객체를 생성 관리 할 수 있는데요, 이렇게 만들어진 하위 클래스를 ConcreteBuilder라고 합니다.
알고리즘 역시 추상화를 통해서 모델의 생성 관리를 할 수 있습니다.
JS 코드를 통해 알아봅시다.
// Product: 자동차 객체
class Car {
constructor() {
this.make = '';
this.model = '';
this.year = '';
}
describe() {
console.log(`Car: ${this.year} ${this.make} ${this.model}`);
}
}
// Builder: 자동차 객체를 생성하기 위한 빌더 인터페이스
class CarBuilder {
constructor() {
this.car = new Car();
}
setMake(make) {
this.car.make = make;
return this;
}
setModel(model) {
this.car.model = model;
return this;
}
setYear(year) {
this.car.year = year;
return this;
}
build() {
return this.car;
}
}
const sportCar = new Builder().setMake('Ferrari').setModel('458').setYear('2023').build();
코드에서는 빌더 객체 sportCar라는 객체를 만들었습니다. 간단한 예시여서 추상화와 알고리즘을 분리하지 않았지만, 이렇게 builder를 통해 여러 객체를 만들 수 있다는 것입니다!
추상 팩토리와 비슷하게 보일 수 있지만, 추상 팩토리는 서브 단계에서 생성 절차가 완료되면 각각의 모델을 바로 반환하는데 반면, 빌더 패턴은 단계별 생성 절차가 모두 완료 되어야 복합 객체를 반환한다는 것입니다.
즉 추상 패턴은 각각의 부품에 의미를 부여하는 반면, 빌더 패턴은 부품이 전부 모여야 의미를 가진다는 것!
❓빌더 패턴의 단점
빌더 패턴은 알고리즘 덕분에 교환 가능성, 그리고 객체군 사이의 의존성을 없앨 수 있어 복잡성을 관리하기 편리합니다.
하지만 이는 작은 규모의 객체나 단순한 객체 생성에는 불필요한 작업일 수 있습니다. 이에 자원 낭비, 오버헤드가 발생할 수 있고, 오히려 유지보수에 어려윰을 가져다 줄 수 있습니다.
그러니 작은 규모의 객체나 생성 로직이 단순한 경우에는 사용하지 않는 것이 좋겠네요.