객체들을 새로운 행동들을 포함한 래퍼 객체들 내에 넣어서 행동들을 연결시키는 디자인 패턴
// 래퍼, 래핑된 객체들에 대한 공통 인터페이스 선언
interface Component {
operation(): string;
}
// 래핑되는 객체들의 클래스이며, 기본적인 행동들을 정의함
// 기본 행동은 데코레이터가 변형할 수 있음
class ConcreteComponent implements Component {
public operation(): string {
return 'ConcreteComponent';
}
}
// 데코레이터 클래스
class Decorator implements Component {
// 래퍼를 참조하기 위한 필드
protected component: Component;
constructor(component: Component) {
this.component = component;
}
// 모든 작업은 래핑된 객체들이 진행하게 됨
public operation(): string {
return this.component.operation();
}
}
// 각 컴포넌트마다 달라지는 행동들은 구상 데코레이터 클래스에 정의함
class ConcreteDecoratorA extends Decorator {
// 오버라이딩을 통해 재정의
public operation(): string {
return `ConcreteDecoratorA(${super.operation()})`;
}
}
class ConcreteDecoratorB extends Decorator {
public operation(): string {
// super.operation()을 통해 상위 객체의 행동을 받아오고, 다른 행동들을 추가함
return `ConcreteDecoratorB(${super.operation()})`;
}
}
// 클라이언트 코드에서는 래핑된 컴포넌트를 통해 하위 데코레이터와 작업을 수행 가능함
function clientCode(component: Component) {
// ...
console.log(`RESULT: ${component.operation()}`);
// ...
}
const simple = new ConcreteComponent();
console.log('Client: I\'ve got a simple component:');
clientCode(simple); // RESULT: ConcreteComponent
console.log('');
const decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log('Client: Now I\'ve got a decorated component:');
clientCode(decorator2); //RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))