우리는 돈을 표현하기 위해 Number를 사용하고 있는데, 한 가지만 생각해보자. 돈은 일반 숫자와는 다르게 0인 경우에 사용할 수 없다. 그리고 초기값이 음수일 수도 없다. 특별한 의미를 가지고 있는 이 숫자를 어떻게 의미있게 포장할 수 있을까? 이 때 나오는 것이 값 객체(Value Object)다.
값 객체가 무조건 한 가지 필드를 가지고 있어야 하는 것은 아니다. 다만, 한 가지의 필드만 있더라도 그 값에 의미가 있다면 값 객체로 포장하는 습관을 들이면 좋다.
const NEGATIVE_AMOUNT_ERROR_MESSAGE = '초기값은 음수가 될 수 없습니다.';
const LOWER_LIMIT_AMOUNT = 0;
class Money {
constructor(private _amount: number) {
if (this._amount < 0) {
throw Error(NEGATIVE_AMOUNT_ERROR_MESSAGE);
}
}
get amount() {
return this._amount;
}
add(other: Money): void {
this._amount += other._amount;
}
reduce(other: Money): void {
if (this._amount - other._amount < LOWER_LIMIT_AMOUNT) {
throw Error(NEGATIVE_AMOUNT_ERROR_MESSAGE);
}
this._amount -= other._amount;
}
}
const INITIAL_MONEY_AMOUNT = 12000;
const BALANCE_PREFIX = '잔액: ';
const store = {
dietCoke: new Money(1800),
coke: new Money(1600),
snack: new Money(1800),
tofu: new Money(1500),
milk: new Money(3800),
};
let money = new Money(INITIAL_MONEY_AMOUNT);
console.log(BALANCE_PREFIX, money.amount);
money.reduce(store.dietCoke);
money.reduce(store.snack);
money.reduce(store.milk);
console.log(BALANCE_PREFIX, money.amount);
의미 있는 값 포장을 통해 Primitive Obsession Anti Pattern을 피할 수 있다. Number나 String을 상수화하거나 값 객체로 포장하여 한 눈에 들어오는 가독성 좋은 코드를 만들자.