반응형
[헤드퍼스트 디자인패턴] 03. 데코레이터 패턴(Decorator Pattern)
1. 데코레이터 패턴이란?
객체에 추가 요소를 동적으로 더할 수 있다. 데코레이터를 사용하면 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있다.
2. 구현 목표
1. 카페의 주문 시스템을 만든다.
2. 고객은 커피를 주문할 때 우유나 두유, 모카를 추가하고 그 위에 휘핑크림을 얹기도 한다.
3. 고려사항
- 추후 옵션이 늘어날 수 있다.
- 각각을 추가할 때마다 커피 가격이 올라가는 점을 고려해야 한다.
4. 구현
4.1 문제가 있는 설계 (상속을 사용 할 경우)
문제점
- 첨가물이 가격이 바뀔 때 마다 기존 코드를 수정해야 한다.
- 첨가물 종류가 많아지면 새로운 메소드를 추가해야 하고, cost() 메소드를 수정해야 한다.
- 새로운 음료가 출시 될 경우, 기존의 첨가물이 들어가면 안되는 경우가 있다. ex) 아이스티가 hasWhip()을 상속받게 됨
- 고객이 더블 모카를 주문할 경우???
4.2 데코레이터 패턴을 활용한 구현
1. DarkRoast 객체에서 시작한다.
2. 손님이 Mocha를 주문했으니 Mocha객체를 만들고 그 객체로 DarkRoast를 감싼다.
3. 손님이 휘핑크림도 같이 주문했으므로 Whip 데코레이터를 만들어 Mocha를 감싼다.
4. 가장 바깥쪽에 있는 데코레이터 Whip의 cost()를 호출한다.
- Whip에서는 Mocha의 cost() 메소드 호출
- Mocha에서는 DarkRoast의 cost() 호출
- DarkRoast에서는 가격과 이름을 반환
- Mocha에서는 DarkRoast의 리턴값과 모카값을 더해 반환
- Whip에서는 Mocha에서 받은 가격에 Whip가격을 더해 최종 가격을 반환
-> 데코레이터와 데코레이터로 감싸는 객체의 형식을 맞추기 위해 Beaverage를 상속 받아 사용한다
4.2.1 코드 구현
public abstract class Beverage {
String description = "제목 없음";
public String getDescription {
return description;
}
public abstract double Cost();
}
public abstract class CondimentDecorator : Beverage {
private Beverage beverage;
public abstract String getDescription();
}
public class Espresso extends Beverage {
public Espresso() {
description = "에스프레소";
}
@Override
public double cost() {
return 1.99;
}
}
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + "모카";
}
@Override
public double cost() {
return beverage.cost() + .20;
}
}
public class Whip extends CondimentDecorator {
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + "휘핑";
}
@Override
public double cost() {
return beverage.cost() + .10;
}
}
public class DecoratorPattern {
public static void main(String[] args) {
Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
System.out.println(beverage.getDescription() + "$" + beverage.cost())
}
}
-> 상속을 이용해 서브클래스를 만들 경우, 그 행동은 컴파일할 때 완전히 결정되고, 모든 서브클래스에서 똑같은 행동을 상속받아야 한다. 그러나 구성으로 객체의 행동을 확장하면 기존 코드를 수정하지 않고 새로운 코드를 만들어 실행 중에 동적으로 행동을 설정 할 수 있다. 기존 코드를 건드리지 않으므로 코드 수정에 따른 버그나 의도하지 않은 부작용을 원천봉쇄할 수 있음.
디자인 원칙
클래스는 확장에 열려있어야 하지만
변경에는 닫혀있어야 한다.
5. 데코레이터 패턴의 특징
- 데코레이터 클래스의 형식은 그 클래스가 감싸는 클래스 형식을 반영한다.
- 데코레이터는 자기가 감싸고 있는 구성 요소의 메소드를 호출한 결과에 새로운 기능음 더함으로써 행동을 확장한다.
- 구성 요소를 감싸는 데코레이터의 개수에는 제한이 없다.
- 구성 요소의 클라이언트는 데코레이터의 존재를 알 수 없다.
- 데코레이터 패턴을 상요하면 자잘한 객체가 매우 많이 추가될 수 있고, 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해 진다. -> 팩토리와 빌더 패턴으로 보완 가능
728x90
반응형
'스터디 > 헤드퍼스트 디자인패턴' 카테고리의 다른 글
[헤드퍼스트 디자인패턴] 06. 커맨드 패턴(Command Pattern) (0) | 2022.07.10 |
---|---|
[헤드퍼스트 디자인패턴] 05. 팩토리 패턴(Factory Pattern) (1) | 2022.07.03 |
[헤드퍼스트 디자인패턴] 04. 싱글턴 패턴(Singleton Pattern) (0) | 2022.06.18 |
[헤드퍼스트 디자인패턴] 02. 옵저버 패턴(Observer Pattern) (0) | 2022.06.12 |
[헤드퍼스트 디자인패턴] 01. 전략패턴 (0) | 2022.06.05 |
댓글