#Decorator Pattern
1. 개념
데코레이터 패턴(Decorator pattern)은 객체의 추가적인 요건을 동적으로 추가하는 패턴이다.
Decorator는 글자 그대로 장식가로서, 중심이 되는 객체가 반환하는 값에
추가적으로 더해져서 결과값을 반환합니다.
쉽게 스타벅스를 생각해보자
스타벅스는 자신만의 커스터마이징 음료를 만들 수 있다.
나같은 경우는 그린 티 크림 프라푸치노(Green Tea Cream Frappuccino)를 주문해서
에스프레소 샷 추가, 저지방 우유, 라이트 시럽, 휘핑크림, 자바칩 갈아 만든
커스터마이징 음료를 산다.
이와 같이 하나의 객체에 옵션들을 여러개 추가해서
만드는 패턴을 장식하는 것과 같다하여
데코레이터 패턴이라 한다.
2. Why? 왜 사용해야 할까?
하나의 객체에 부가적인 기능을 덧붙여야 할 때가 있다.
또한 동일한 객체를 여러개 만들어야 하지만
각기 다른 기능을 첨가할 필요가 있을 때
데코레이터 패턴을 이용하면
많은 객체에게 다양한 부가기능을 쉽고 빠르게 적용할 수 있다.
3. 장단점
데코레이터 패턴의 핵심은
중심이 되는 객체를 놔두고 추가적인 사항을 첨가하는 방식이라
객체에 기능을 추가 또는 삭제 할 때
중심이 되는 객체를 수정하지 않고 동적으로 추가 또는 삭제할 수 있다.
객체지향의 원칙 중 OCP에 충실한 패턴인데
OCP는 Open-Close Principle로서 확장에는 열려있고 변경에는 닫혀 있어야 한다는 원칙이다.
데코레이터 패턴을 사용하게 되면
기존 객체는 변경하지 않고 자유로운 확장을 구현할 수 있으며
기존 객체가 수정되더라도 또는 데코레이터가 수정되더라도
해당 클래스의 내용만 수정해주면 되기 때문에 변경에 대해서도 닫혀있다.
또한,
기본적인 데이터에 첨가하는 데이터가 일정하지 않고 다양할 때
데코레이터 패턴을 사용하면 효율적으로 사용할 수 있다.
반면에
데코레이터를 너무 많이 사용하게 되면
자질한 객체가 많이 추가될 수 있고
오히려 코드가 복잡해 질 수 있다는 단점이 있다.
4. 추가내용
비슷하게 확장을 하는 상속과 데코레이터 패턴의 차이는 무엇일까?
상속은 하위 클래스를 생성하는 방법으로 확장을 하며
데코레이터 패턴은 기본 객체에 추가하는 객체를 생성하는 방법으로 확장을 한다.
상속은 오버라이딩을 통해 기능을 전부 명시해 줘야 하지만
데코레이터 패턴은 기본 기능은 놔두고 추가되는 기능만 명시해 줄 수 있다는 차이가 있다.
데코레이터 패턴은 여러 패턴들과 유사한 구조를 가지고 있다.
다양하고 일정하지 않은 여러 객체들을 하나의 객체로 묶는 것을 Composite 패턴이라 하는데
데코레이터 패턴은 한 구성요소 만을 갖는 Composite 패턴이라 볼 수 있다.
그러나 목적에서 차이가 있다.
Composite 패턴은 목적이 생성되어 있는 객체들 간의 합성에 있고
데코레이터 패턴은 목적이 객체에 새로운 행동을 추가하는 데에 있다.
Strategy 패턴은 유사한 행위를 캡슐화하는 인터페이스를 정의하여
객체들의 행위를 유연하게 확장하는 방법으로 데코레이터 패턴과 구조가 비슷하다.
하지만 Strategy 패턴은 객체의 내부를 변화시키며
데코레이터 패턴은 새로운 객체를 추가하여 객체를 변경한다는 점에서 차이가 있다.
마지막으로 Adaptor 패턴은 서로 다른 인터페이스를 통일시켜 사용하는 패턴으로서
데코레이터가 기준 객체와 부가 객체를 연결하는 어댑터 역할을 하는 것과 유사하다.
그러나 데코레이터 패턴은 어댑터 패턴과 달리 인터페이스를 변경 없이
객체에 새로운 행동을 추가한다는 점에서 차이가 있다.
5. 클래스 다이어그램
6. 예제 코드
스타벅스에서 그린티 크림 프라푸치노에
샷추가, 저지방 우유, 자바칩을 추가했을 때 가격을 알아본다.
public interface StarbucksMenu {
public int cost();
}
public class GreenTeaCreamFrappuccino implements StarbucksMenu{
public GreenTeaCreamFrappuccino() {
System.out.println("그린티 크림 프라푸치노를 선택하셨습니다.");
}
@Override
public int cost() {
System.out.println("그린티 크림 프라푸치노 가격 : 5000");
return 5000;
}
}
public abstract class CoffeeDecorator implements StarbucksMenu{
public abstract int cost();
}
스타벅스 메뉴 인터페이스에
기본메뉴인 그린티 크림 프라푸치노와
부가메뉴인 커피데코레이터가 연결되어있다.
public class EspressoShot extends CoffeeDecorator{
StarbucksMenu starbucksMenu;
public EspressoShot(StarbucksMenu starbucksMenu) {
System.out.println("샷 추가 + 1000원 추가");
this.starbucksMenu = starbucksMenu;
}
@Override
public int cost() {
return starbucksMenu.cost() + 1000;
}
}
public class JavaChip extends CoffeeDecorator{
StarbucksMenu starbucksMenu;
public JavaChip(StarbucksMenu starbucksMenu) {
System.out.println("자바 칩 +500원 추가");
this.starbucksMenu = starbucksMenu;
}
@Override
public int cost() {
return starbucksMenu.cost() + 500;
}
}
public class LowFatMilk extends CoffeeDecorator{
StarbucksMenu starbucksMenu;
public LowFatMilk(StarbucksMenu starbucksMenu) {
System.out.println("저지방 우유 +300원 추가");
this.starbucksMenu = starbucksMenu;
}
@Override
public int cost() {
return starbucksMenu.cost() + 300;
}
}
추상클래스인 커피데코레이터의 구현은
자식클래스인 자바칩, 저지방우유, 에스프레소샷에 구현된다.
public class Customer {
public static void main(String[] args) {
System.out.println("그린티 프라푸치노에 자바칩, 저지방우유, 샷 추가 해주세요.");
StarbucksMenu order = new EspressoShot(new LowFatMilk(new JavaChip(new GreenTeaCreamFrappuccino())));
System.out.println("총 가격은 " + order.cost() + "입니다.");
}
}
고객이 위와 같이 주문을 하면
기본 메뉴를 먼저 생성한 후
기본 메뉴에 추가메뉴를 더해 총 가격을 구할 수 있다.
결과 창은 다음과 같다.
7. 참고
https://lalwr.blogspot.com/2016/02/decorator-pattern.html
https://meaownworld.tistory.com/91
https://meylady.tistory.com/53
https://free-strings.blogspot.com/2016/04/adapter-decorator-facade-proxy.html
https://jusungpark.tistory.com/9
Made by 꿩
'스터디 > GoF의 디자인패턴' 카테고리의 다른 글
Chain of Responsibility (0) | 2019.05.06 |
---|---|
Strategy Pattern (0) | 2019.04.30 |
Template Method Pattern (0) | 2019.04.30 |
Factory Method Pattern (0) | 2019.04.23 |