[디자인 패턴] 데코레이터 패턴
이 글은 카페 주문 시스템에 데코레이터 패턴을 적용한 예제를 이용해 개념을 설명합니다.
문제 제기
아래의 클래스 다이어그램은 모 카페의 초창기 주문 시스템 클래스의 구성이다.
클래스 다이어그램 설명
- Beverage 클래스는 음료를 나타내는 추상 클래스
- 매장에서 판매되는 모든 음료는 Beverage 클래스의 서브 클래스
이 구성의 문제점
- 음료의 종류 뿐 아니라 고객이 추가하는 옵션(우유, 두유, 모카, 휘핑크림 등)이 들어간 모든 경우의 수에 서브 클래스를 작성해야 하므로 클래스의 수가 너무 많아짐.
- 첨가물 가격이 바뀔 때마다 기존 코드를 수정해야 함. => OCP 원칙 위반
OCP(Open-Closed Principle)는 '소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다'는 프로그래밍 원칙
장식하기(decorate)
- DarkRoast 객체를 가져옴. - beverage로 부터 상속
- 첨가물 Mocha 객체로 장식. - decorator
- 첨가물 Whip 객체로 장식. - decorator
- cost()를 호출. 이 때 첨가물의 가격을 계산하는 일은 해당 객체에게 위임.
데코레이터 패턴 정의
데코레이터 패턴(Decorator Pattern)으로 객체에 추가 요소를 동적으로 더할 수 있음.
데코레이터를 사용하면 서브 클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있음.
- 데코레이터의 슈퍼클래스는 자신이 장식하고 있는 객체의 슈퍼클래스와 같음.
- 한 객체를 여러개의 데코레이터로 감쌀 수 있음.
- 데코레이터는 자신이 감싸고 있는 객체와 같은 슈퍼 클래스를 가지고 있어 원래 객체(싸여있는 객체)가 들어갈 자리에 데코레이터 객체를 넣어도 무관.
- 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업을 수행할 수 있음.
- 객체는 언제든지 감쌀 수 있으므로 실행 중에 필요한 데코레이터를 마음대로 적용할 수 있음.
데코레이터 패턴 적용
예제 코드
Base
public abstract class Beverage {
protected String description = "음료입니다.";
public String getDescription() {
return description;
}
public abstract double cost();
}
public abstract class CondimentDecorator extends Beverage {
protected Beverage beverage;
public abstract String getDescription();
}
Beverages
public class Espresso extends Beverage {
public Espresso() {
description = "에스프레소 커피";
}
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "하우스 블렌드 커피";
}
public double cost() {
return 0.89;
}
}
Condiments
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", 모카";
}
public double cost() {
return beverage.cost() + 0.20;
}
}
public class Whip extends CondimentDecorator {
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", 휘핑크림";
}
public double cost() {
return beverage.cost() + 0.15;
}
}
예제용 클래스
public class Coffee {
public Coffee() {
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + ": $" + espresso.cost());
Beverage darkRoast = new Whip(new Mocha(new HouseBlend()));
System.out.println(darkRoast.getDescription() + ": $" + darkRoast.cost());
}
}
예제 출력
에스프레소 커피: $1.99
하우스 블렌드 커피, 모카, 휘핑크림: $1.24
단점
- 잡다한 클래스가 엄청나게 추가되어 이해하기 힘든 디자인이 만들어질 수 있음.
- 특정 형식에 의존하는 코드에 데코레이터를 무턱대고 적용하면 모든게 엉망이 됨.
- 구성 요소를 초기화하는데 필요한 코드가 훨씬 복잡해질 수 있음.
장점
- 객체에 추가 요소를 동적으로 더할 수 있음. => OCP 원칙에 부합
- 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있음.
참조
- 헤드퍼스트 디자인패턴 (도서)
Author And Source
이 문제에 관하여([디자인 패턴] 데코레이터 패턴), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sapcy/dp-decorator저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)