데코레이터 (Decorator)
"기존 객체의 동작을 동적으로 증대시키는 것으로 구성된 구조적 디자인 패턴"
동작이 해당 클래스의 모든 객체에 적용되지 않음
데코레이팅 될 경우 인스턴스에만 추가됨 => 클래스의 상속과는 다름
프록시 패턴과 매우 유사. 개선 대신 새로운 기능으로 확장을 함
1. 데코레이터 구현 기법
프록시와 데코레이터는 동일한 구현 전략
이전의 StackCalculator에서 add() 새 함수 노출, divide() 함수 호출 가로채 0으로 나누는지 검사
1-1. 컴포지션
컴포지션 사용 시 대상 컴포넌트는 이를 상속한 새로운 객체로 감싸 사용
원래 컴포넌트에 위임하면서 새로운 함수를 정의함
class EnhancedCaculator {
consturctor (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.getValue();
if (divisor === 0) {
throw Error('Division by 0');
}
// Subject 에 대한 유효한 위임자일 경우
return this.calculator.divide();
}
// 위임된 함수들
putValue(value) {
return this.calculator.putValue(value);
}
getValue() {...}
peekValue() {...}
// ...
}
const calculator = new StackCalculator();
const enhancedCalculator = new EnhancedCaculator(calculator);
enhancedCalculator.putValue(4);
console.log(enhancedCalculator.add());
enhancedCalculator.putValue(3);
enhancedCalculator.putValue(2);
console.log(enhancedCalculator.multiply());
위에서 말했다시피 프록시 패턴과 컴포지션 구현은 매우 비슷함
(~싶이 - 짐작에서 사용, ~시피 - 지각을 나타내는 동사와 결합)
1-2. 객체 확장 (Argumentation)
직접 새 함수를 정의(몽키패치)해 데코레이트 수행
function patchCalculator (calculator) {
// 새로운 함수
calculator.add = function () {
const addEnd1 = calculator.getValue();
const addEnd2 = calculator.getValue();
cosnt result = addEnd1 + addEnd2;
calculator.putValue(result);
return result;
}
// 수정된 함수
const divideOrig = calculator.divide;
calculator.divide = () => {
// 추가적 검증 로직
cosnt divisor = calculator.peekValue();
if (divisor === 0) {
throw Error('Division by 0');
}
// Subject 에 대한 유효한 위임자-delegates 일 경우
return this.calculator.divide();
}
return calculator;
}
const calculator = new StackCalculator();
const enhancedCalculator = pathchCalculator(calculator);
// ...
calculator와 enhancedCalculator 는 동일한 객체를 참조 === true
원래의 계산기 객체를 변형한 후 반환하기 때문
1-3. Proxy 객체를 이용한 데코레이팅
Proxy 객체를 이용해 객체를 데코레이트 가능
const enhancedCalculatorHandler = {
get (target, property) {
IF (property == 'add) {
// 새로운 함수
return function add() {
const addend2 = target.getValue();
cosnt addend1 = target.getValue();
const result = addend1 + addend2;
target.getValue(result);
return result;
}
} else if (property == 'divide) {
/// 수정된 함수
return function () {
const divisor = target.peekValue();
if (divisor === 0) throw new Error('Division by 0');
return target.divide();
}
}
// 위임된 함수들과 속성들
return target[property];
}
}
const calculator = new StackCalculator();
const enhancedCalculator = new Proxy(
calculator,
enhancedCalculatorHandler
)
// ...
프록시 패턴과 동일한 주의 사항이 적용됨
2. LevelUP DB 데코레이터
LevelUP & LevelDB 소개
- 키-값 저장소인 구글의 LevelDB 를 각ㅁ싼 Node.js 래퍼임
- 최소주의 확장성이 목표
- 가장 기본적인 기능만을 제공
LevelUp 플러그인 구현
데코레이터 패턴 사용, LevelUP용 플러그인 만듦
객체 확장 기술을 사용
이번에 만들어볼 것 : 특정패턴의 객체가 DB 에 저장될때마다 알림 받기
// {a:1} 가 포함되어 있을 경우 알림 발생, Ex) {a:1, b:2} 알람 발생
// 제공된 인스턴스 함수를 직접 연결하면 됨 = 객체 확장
export function levelSbuscribe(db) {
db.subscribe = (pattern, listener) => {
// 모든 입력작업을 수신
db.on('put', (key, val) => {
// 패턴매칭 알고리듬 수행
const match = Object.keys(pattern).every(
k => (pattern[k] === val[k]);
)
// 일치 시 리스너에 알림
if (match) {
listener(key,val);
}
})
}
return db;
}
// index.js
// 플러그인 사용
import { dirname, join} from 'path';
import { fileURLToPath} from 'url';
import level from 'level';
import { levelSbuscribe} from './level-subscribe';
const __dirname = dirname(fileURLToPath(import.meta.url));
// 파일 저장위치, 기본 인코딩 설정
const dbPath = join(__dirname, 'db');
const db = level(dbPath, { valueEncoding: 'json' });
levelSbuscribe(db); // 데코레이터 연결
db.subscribe( // 플러그인 제공 기능 사용
{ doctype : 'tweet', language: 'en' },
(k, val) => console.log(val);
)
db.put('1', {
doctype : 'tweet',
text: 'Hi',
language : 'en'
});
db.put('2', {
doctype: 'compan'y,
name : 'ACME Co',
});
*put 작업 및 배치작업에 쉽게 확장 가능
Example Packages
-level-inverted-index : db 저장값에 대한 텍스트 검색 수행 가능하도록 역 인덱스 추가 플러그인
-levelplus : db 원자 연산으로 업데이트를 추가하는 플러그인
-json-socket : TCP 소켓을 통해 JSON 데이터 더 쉽게 보낼수 있음
-fastify : Fastify 서버 인스턴스를 데코레이트하기 위한 하나의 API 를 노출하고 있는 웹 어플 프레임워크
3. 프록시와 데코레이터 사이의 경계
두개는 매우 유사함. 대부분 차이는 런타임에 사용되는 방식에 기인
데코레이터 : 새로운 동작을 기존의 객체에 추가
래퍼로 볼 수 있음 - 다양한 유형의 객체 가져와 데코레이터로 감싸 추가적인 기능 추가
프록시 : 고정적이거나 가상의 객체에 접근을 제어
객체 접근 제어
원래 인터페이스 수정 안함 => 원래 객체를 참조하는 다른객체가 안전함
노드는 두 패턴의 경계가 모호함 그냥 섞어서 잘 쓰셈. 상호보완이 잘될거임
'Javascript > Node.js' 카테고리의 다른 글
Strategy Pattern - 행위 디자인 패턴 (0) | 2022.02.09 |
---|---|
Adaptor Pattern - 구조적 설계 패턴 (0) | 2022.02.09 |
Proxy Pattern - 구조적 설계 패턴 (0) | 2022.02.06 |
Wiring Pattern - 생성자 디자인 패턴 (0) | 2022.02.03 |
Singleton Pattern - 생성자 디자인 패턴 (0) | 2022.02.03 |
댓글