티스토리 뷰
빌더 패턴
복잡한 객체를 단계적으로 생성할 수 있도록 하는 패턴
객체 생성시 생성자함수 대신에 빌더클래스를 통하여 객체를 생성할 수 있게함
객체의 멤버변수에 대한 가독성, 유지보수를 높임
예제
class User {
private name: string;
private age : number;
constructor(builder: UserBuilder) {
this.name = builder.name;
this.age = builder.age;
}
}
class UserBuilder {
public name: string;
public age: number;
setName(name) {
this.name = name;
return this;
}
setAge(age) {
this.age = age;
return this;
}
build() {
return new User(this)
}
}
const user = new UserBuilder().setName("css").setAge(5).build();
- User의 객체를 Builder를 통해 만든다.
- 생성된 User객체는 불변하다(굳이 바꿀라면 바꿀 수 있음)
- 기존 생성자코드가
new User("css", 5)
였을 때 weight을 추가하는 요구사항 발생시 해당하는 생성자코드 파라미터들을 모두 변경해주어야함- 패턴 적용시
setWeight
만 붙여주면 됨
- 패턴 적용시
객체 생성시 당장에 초기화되지 않아도될 멤버변수들때문에 생성자코드에 undefined 또는 null을 넣는 방법보다 해당 방법사용시 좀 더 명확하게 나타낼 수 있음.
멤버변수 초기화시 검증부분을 메소드별로 분리할 수 있음(생성자에 몰아넣는 방식이 아님)
생성자를 대신하는 것으로 끝나는게 아니라 객체 자체를 반환해서 체이닝으로 메소드들을 수행하는 기법을 사용할 수 있음
클래스 수가 *2배만큼 늘어나기 때문에 코드복잡성이 늘어남.
필드갯수가 적고(최소 4개정도) 변경 가능성이 없는 경우라면 생성자를 사용하는 것이 더 좋을 수 있음
Zod
zod의 경우 빌더패턴과 유사해보이나 Zod클래스 생성하는 빌더가 아닌 본 클래스에서 객체를 생성하기 위한 메소드체이닝을 사용하고 있다.
재밌는 점은 메소드 체이닝을 위해
return this
를 하는 것이 아닌 매번 함수에서 새로운 객체를 생성하고 있다는 점이다. 이전 메소드들의 상태를 저장하기위해 checks에 추가해 객체를 생성하는 것을 볼 수 있다.
// ZodNumber클래스 메소드 일부
_addCheck(check: ZodNumberCheck) {
return new ZodNumber({
...this._def,
checks: [...this._def.checks, check],
});
}
int(message?: errorUtil.ErrMessage) {
return this._addCheck({
kind: "int",
message: errorUtil.toString(message),
});
}
// ZodNumber클래스 메소드 일부
const numberSuite = new Benchmark.Suite("z.number");
const numberSchema = z.number().int();
numberSuite
.add("valid", () => {
numberSchema.parse(1);
})
.add("invalid type", () => {
try {
numberSchema.parse("bad");
} catch (e) {}
})
.add("invalid number", () => {
try {
numberSchema.parse(0.5);
} catch (e) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.number: ${e.target}`);
});
빌더 패턴을 사용한 Validator 예제
생성자를 통해 검증하고싶은 조건들을 미리 검증해놓아서 공용된 검증에 사용할 수 있는 밸리데이터를 만들 수 있다.
Validator 인터페이스
interface Validator {
validate(value: any): boolean;
}
NumberValidator 클래스
class NumberValidator implements Validator {
private isNumberCheck: boolean = false;
private isPositiveCheck: boolean = false;
private constructor() {}
static builder() {
return new NumberValidator();
}
public isNumber(): this {
this.isNumberCheck = true;
return this;
}
public isPositive(): this {
this.isPositiveCheck = true;
return this;
}
public validate(value: any): boolean {
if (this.isNumberCheck && typeof value !== 'number') {
return false;
}
if (this.isPositiveCheck && value <= 0) {
return false;
}
return true;
}
}
StringValidator 클래스
class StringValidator implements Validator {
private isStringCheck: boolean = false;
private isNotEmptyCheck: boolean = false;
private constructor() {}
static builder() {
return new StringValidator();
}
public isString(): this {
this.isStringCheck = true;
return this;
}
public isNotEmpty(): this {
this.isNotEmptyCheck = true;
return this;
}
public validate(value: any): boolean {
if (this.isStringCheck && typeof value !== 'string') {
return false;
}
if (this.isNotEmptyCheck && value.trim().length === 0) {
return false;
}
return true;
}
}
ValidatorBuilder 클래스
class ValidatorBuilder {
public static number(): NumberValidator {
return NumberValidator.builder();
}
public static string(): StringValidator {
return StringValidator.builder();
}
}
사용 예제
const numberValidator = ValidatorBuilder.number()
.isNumber()
.isPositive();
console.log(numberValidator.validate(15)); // true
console.log(numberValidator.validate(-5)); // false
const stringValidator = ValidatorBuilder.string()
.isString()
.isNotEmpty();
console.log(stringValidator.validate("Hello")); // true
console.log(stringValidator.validate("")); // false
빌더패턴을 이용해 생성자를 메소드체이닝으로 명확히 표현할 수 있고 변경에 용이하게 대처할 수 있다는 점이 있다.
분리된 setter들로인해 생성자의 검증을 분산시킬 수 있다.
객체의 불변성을 보장할 수 있다.
'프로그래밍' 카테고리의 다른 글
브릿지 패턴 (0) | 2024.07.19 |
---|---|
싱글톤 패턴 (0) | 2024.07.11 |
브라우저에서 대용량 파일 다루기 - 개념편 (0) | 2022.07.22 |
한 컴퓨터에서 여러 Git 계정 사용하기 with script (0) | 2022.07.21 |
Emscripten으로 C++코드 JS로 포팅하기(2) - EMSCRIPTEN_BINDINGS (0) | 2022.07.10 |
- Total
- Today
- Yesterday