데코레이터 패턴을 JavaScript에서 구현한 예시 코드
class StackCalculator {
constructor () {
this.stack = []
}
putValue(value) {
this.stack.push(value)
}
getValue() {
return this.stack.pop()
}
peekValue() {
return this.stack[this.stack.length - 1]
}
clear() {
this.stack = []
}
divide() {
const divisor = this.getValue()
const dividend = this.getValue()
const result = dividend / divisor
this.putValue(result)
return result
}
multiply() {
const multiplicand = this.getValue()
const multiplier = this.getValue()
const result = multiplier * multiplicand
this.putValue(result)
return result
}
}
이제 위 StackCalculator
에 add
동작을 추가하고, divide
에 validation을 추가하는 데코레이터를 세 가지 방법으로 구현하겠다.
class EnhancedCalculator {
constructor(calculator) {
this.calculator = calculator
}
// 새로운 함수
add() {
const addend2 = this.getValue()
const addend1 = this.getValue()
const result = addend1 + addend2
this.putValue(result)
return result
}
// 수정된 함수
divide() {
// 추가적인 검증 로직
const divisor = this.calculator.peekValue()
if (divisor === 0) {
throw Error('Division by 0')
}
// Subject에 대한 유효한 위임자(delegates)일 경우
return this.calculator.divide()
}
// 위임된 함수들
putValue(value) {
return this.calculator.putValue(value)
}
getValue() {
return this.calculator.getValue()
}
peekValue() {
return this.calculator.peekValue()
}
clear() {
return this.calculator.clear()
}
multiply() {
return this.calculator.multiply()
}
}
const calculator = new StackCalculator()
const enhancedCalculator = new EnhancedCalculator(calculator)
enhancedCalculator.putValue(4)
enhancedCalculator.putValue(3)
console.log(enhancedCalculator.add())
enhancedCalculator.putValue(2)
console.log(enhancedCalculator.multiply())
function patchCalculator(calculator) {
// 새로운 함수
calculator.add = function () {
const addend2 = calculator.getValue()
const addend1 = calculator.getValue()
const result = addend1 + addend2
calculator.putValue(result)
return result
}
// 수정된 함수
const divideOrig = calculator.divide
calculator.divide = () => {
// 추가적인 검증 로직
const divisor = calculator.peekValue()
if (divisor === 0) {
throw Error('Division by 0')
}
// Subject에 대한 유효한 위임자(delegate)일 경우
return divideOrig.apply(calculator)
}
return calculator
}
const calculator = new StackCalculator()
const enhancedCalculator = new patchCalculator(calculator)
// ...
const enhancedCalculatorHandler = {
get(target, property) {
if (property === 'add') {
// 새로운 함수
return function add() {
const addend2 = target.getValue()
const addend1 = target.getValue()
const result = addend1 + addend2
this.putValue(result)
return result
}
} else if (property ==='divide') {
// 수정된 함수
return function () {
// 추가적인 검증 로직
const divisor = target.peekValue()
if (divisor === 0) {
throw Error('Division by 0')
}
// Subject에 대한 유효한 위임지(delegates)일 경우
return target.divide()
}
}
// 위임된 함수들과 속성들
return target[property]
}
}
const calculator = new StackCalculator()
const enhancedCalculator = new Proxy(
calculator,
enhancedCalculatorHandler)
// ...
Mario Cascaiaro, Luciano Mammino, Node.js 디자인패턴 바이블 3판