[JAVA/디자인패턴] Decorator Pattern, 데코레이터 패턴
Decorator Pattern
객체에 추가적인 책임을 동적으로 부여할 수 있도록 설계한다.
데코레이터는 서브클래스(상속)을 사용하지 않아도 유연하고 융통성 있는 기능 확장을 가능하게 한다.
- 상속으로 문제를 풀면 너무 많은 상속 관계가 발생할 수 있음
- 상속을 사용하지 않고, 새로운 기능을 추가하는 것이 목표
- 데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 추가할 수 있도록 함
- 데코레이터 패턴에서의 상속은 기능(행동)을 물려받기 위해서가 아니라 형식을 맞추기 위해서 사용된다.
1. Component : 각 구성요소는 직접 쓰일 수도 있고 데코레이터로 감싸져서 쓰일 수도 있음. 클래스 또는 인터페이스
2. ConcreteComponent : 새로운 행동을 동적으로 추가
3. Decorator : 자신이 장식할 구성요소와 같은 인터페이스 또는 추상 클래스 역할을 한다. Component의 기능, 상태를 확장할 수 있다.
4. ConcreteDecorator : Decorator을 종류별로 구현하며, 객체가 장식하고 있는 것(데코레이터가 감싸고 있는 Component)를 멤버변수로 가진다.
카페 음료 : Decorator Pattern 적용 이전
카페에서 파는 많은 음료수들을 생각해보자. 하우스블랜드, 다크로스트, 디카페인, 에스프레소 등 많은 커피의 종류에, 드리즐, 휘핑, 모카 등토핑을 추가할 수도 있을 것이다.
위와 같이 Beverage를 구현하는 클래스들이 있다고 가정해보자. 각각의 음료들은 가격을 계산하는 cost()메서드를 작성해야 한다고 생각해보자.
문제점
1. 만약 추가적으로 휘핑크림을 얹을 수 있도록 한다면, 기존 4개의 클래스에, 휘핑이 추가된 4개의 클래스가 추가적으로 구현되어야 할 것이다.
2. 휘핑크림 토핑의 가격이 인상된다면, 위의 4개의 클래스의 cost() 메서드를 모두 변경해야 한다.
3. 여기서 새로운 토핑이 또 추가된다면 관리하기가 매우 어렵다.
카페 음료 : Decorator Pattern 적용
특정 음료에서 시작해서, 첨가물로 음료를 장식(Decorate)하도록 한다.
Component 작성
//Component
public abstract class Beverage {
String description = "제목 없음";
public String getDescription() {
return description;
}
public abstract double cost();
}
Component에 해당하는 Beverage 추상 클래스이다.
음료는 가격을 얻어낼 수 있어야 하며, 음료의 이름을 얻을 수 있어야 한다.
ConcreteComponent 작성
//ConcreteComponent
public class BevarageEspresso extends Beverage{
public BevarageEspresso(){
this.description = "에스프레소";
}
@Override
public double cost() {
return 1000;
}
}
//ConcreteComponent
public class BevarageDarkRoast extends Beverage{
public BevarageDarkRoast(){
this.description = "다크로스트";
}
@Override
public double cost() {
return 1000;
}
}
ConcreteComponent에 해당하는 클래스들이다. 위에서 작성한 Component인 추상 클래스 Beverage를 상속받으며, 음료 별로 상이한 이름과, cost를 얻어낼 수 있도록 한다.
Decorator 작성
//Decorator
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
자신이 장식할 구성요소(Beverage)와 같은 인터페이스 또는 추상 클래스 역할을 하는 기본 Decorator 추상 클래스를 작성한다.
//ConcreteDecorator
public class WhipDecorator extends CondimentDecorator{
Beverage beverage;
public WhipDecorator(Beverage beverage){
this.beverage = beverage;
}
@Override
public double cost() {
return this.beverage.cost() + 300;
}
@Override
public String getDescription() {
return "휘핑 " + this.beverage.getDescription();
}
}
//ConcreteDecorator
public class MochaDecorator extends CondimentDecorator{
Beverage beverage;
public MochaDecorator(Beverage beverage){
this.beverage = beverage;
}
@Override
public double cost() {
return this.beverage.cost() + 500;
}
@Override
public String getDescription() {
return "모카 " + this.beverage.getDescription();
}
}
Decorator을 구현하는 구체적인 클래스를 작성한다. Beverage를 멤버변수로 가지며, 이를 기반으로 Wrapping 할 수 있다.
위처럼 데코레이터 패턴을 적용할 시, Beverage에 해당하는 DarkRoast를 Mocha와 Whip이라는 Decorater들로 감싸서(Wrapping)하여 확장할 수 있다.
public class Main {
public static void main(String[] args){
Beverage b = new BevarageDarkRoast();
System.out.println(b.getDescription() + " " + b.cost()); //다크로스트 1000.0
b = new MochaDecorator(b);
System.out.println(b.getDescription() + " " + b.cost()); //모카 다크로스트 1500.0
b = new WhipDecorator(b);
System.out.println(b.getDescription() + " " + b.cost()); //휘핑 모카 다크로스트 1800.0
}
}
1. WhipDecorator는 멤버변수로 "장식하고 있는 대상인 MochaDecorator(BeverageDarkRoast)"를 가지고 있다.
- getCost() 시 MochaDecorator(BeverageDarkRoast)의 getCost()와 휘핑토핑의 추가금액을 더해 반환하게 된다.
2. MochaDecorator은 멤버변수로 "장식하고 있는 대상인 BeverageDarkRoast 를 가지고 있다.
- getCost() 시 BeverageDarkRoast의 getCost()와 모카토핑의 추가금액을 더해 반환하게 된다.
3. BeverageDarkRoast은 컴포넌트를 구현하는 클래스로, 기본 동작을 구현하고 있어, 고유의 getCost()가 구현되어 있다.
= WhipDecorator(MochaDecorator(BeverageDarkRoast)))
'CS > Design pattern' 카테고리의 다른 글
[JAVA/디자인패턴] Adapter Pattern, 어댑터 패턴 (2) | 2023.11.27 |
---|---|
[JAVA/디자인패턴] Factory : 팩토리 메서드 패턴, 추상 팩토리 패턴 (0) | 2023.10.20 |
[JAVA/디자인패턴] Observer Pattern, 관찰자 패턴 (1) | 2023.10.06 |
[JAVA/디자인패턴] Strategy Pattern, 전략 패턴 (1) | 2023.10.05 |
[JAVA/디자인패턴] Iterator Pattern, 반복자 패턴 (1) | 2023.10.05 |