객체에 새로운 책임을 동적으로 추가할 때 사용
1. 의도
•
GoF
◦
객체에 동적으로 새로운 책임을 추가 할 수 있게 합니다. 기능을 추가하려면, 서브클래스를 생성하는 것보다 융통성 있는 방법을 제공합니다.
•
Head First
◦
객체에 추가 요소를 동적으로 더 할 수 있습니다.
◦
데코레이터를 사용하면 서브클래스를 만들때보다 훨씬 유연하게 기능을 확장할 수 있습니다.
2. 활용성
•
동적으로 또한 투명하게, 다시 말해 다른 객체에 영향을 주지 않고 개개의 객체에 새로운 책임을 추가하기 위해 사용합니다.
•
제거될 수 있는 책임에 대해 사용합니다.
•
실제 상속으로 서브클래스를 계속 만드는 방법이 실질적이지 못할 때 사용합니다.
◦
너무 많은 수의 독립된 확장이 가능할 때 모든 조합을 지원하기 위해 상속으로 해결하면 클래스 수가 폭발적으로 많아지게 됩니다.
◦
아니면 클래스 정의가 숨겨지든가, 그렇지 않더라도 서브클래싱을 할 수 없게 됩니다.
3. 구조
•
UML Class Diagram
4. 참여자
참여자 | 역할 | 예시 |
Component | 동적으로 추가할 서비스를 가질 가능성이 있는 객체들에 대한 인터페이스 | 자동차 |
ConcreteComponent | 추가적인 서비스가 실제로 정의되어야 할 필요가 있는 객체 | SM3 |
Decorator | Component 객체에 대한 참조자를 관리하면서 Component에 정의된 인터페이스를 만족하도록 인터페이스를 정의 | GPS 옵션, 시트 옵션 |
ConcreteDecorator | Component에 새롭게 추가될 서비스를 실제로 구현하는 클래스 | 현대 네비게이션, 가죽시트 |
5. 협력 방법
•
Decorator는 자신의 Component 객체 쪽으로 요청을 전달합니다. 요청 전달 전 및 전달 후에 자신만의 추가 연산을 선택적으로 수행할 수 도 있습니다.
6. 결과
•
Pros
◦
단순한 상속보다 설계의 융통성을 더 많이 증대
▪
데코레이터 - 객체에 새로운 행동을 추가할 수 있는 가장 효과적인 방법
◦
클래스 계통의 상부측 클래스에 많은 기능이 누적되는 상황 회피 가능
▪
책임 추가 작업에서 필요한 비용만 그때 지불하는 방법을 제공
•
Cons
◦
데코레이터와 해당 그 데코레이터의 구성요소가 동일한 것은 아님
▪
데코레이터는 사용자에게 일관된 인터페이스를 제공하는 껍데기
▪
객체 식별자의 관점에서 구성요소와 이를 둘러싼 데코레이터 객체가 동일한 식별자를 가질 필요는 없음
◦
데코레이터를 사용함으로써 작은 규모의 객체들이 많이 생김
▪
클래스들이 어떻게 조합하여 새로운 모습과 기능을 만들어내는가에 따라서 새로운 객체가 계속 만들어짐
▪
객체들을 잘 이해하고 있다면 시스템 정의가 쉬우나, 그렇지 않으면 객체들을 모두 이해하고 수정하는 과정이 복잡함
7. 예시 코드
// Base Car class
class Car {
public description: string;
constructor() {
this.description = "Basic car";
}
public cost(): number {
return 10000;
}
}
// Decorator class for GPS system
class GPSDecorator extends Car {
private car: Car;
constructor(car: Car) {
super();
this.car = car;
}
public get description(): string {
return `${this.car.description}, GPS`;
}
public cost(): number {
return this.car.cost() + 500;
}
}
// Decorator class for leather seats
class LeatherSeatsDecorator extends Car {
private car: Car;
constructor(car: Car) {
super();
this.car = car;
}
public get description(): string {
return `${this.car.description}, leather seats`;
}
public cost(): number {
return this.car.cost() + 1000;
}
}
// Client code
const basicCar = new Car();
console.log(basicCar.description, basicCar.cost()); // Basic car 10000
const carWithGPS = new GPSDecorator(basicCar);
console.log(carWithGPS.description, carWithGPS.cost()); // Basic car, GPS 10500
const carWithLeatherSeats = new LeatherSeatsDecorator(basicCar);
console.log(carWithLeatherSeats.description, carWithLeatherSeats.cost()); // Basic car, leather seats 11000
const carWithGPSAndLeatherSeats = new LeatherSeatsDecorator(new GPSDecorator(basicCar));
console.log(carWithGPSAndLeatherSeats.description, carWithGPSAndLeatherSeats.cost()); // Basic car, GPS, leather seats 11500
TypeScript
복사