객체에 추가요소를 동적으로 더할 수 있다. 훨씬 유연하게 기능을 확장할 수 있다.
예제로 학습해 봅시다.
카페의 주문 시스템을 만든다.
고객은 음료를 주문할 때 우유, 두유, 모카, 휘핑크림등을 추가할 수 있다.
추후 옵션이 늘어날 수 있다.
각각을 추가할 때마다 커피 가격이 올라가는 점을 고려해야 한다.
상속을 사용한 설계를 보자
Beverage클래스에 우유, 두유 등등 추가할 수 있는 것들에 대한 boolean변수를 만들고 getter/setter를 추가했다. 그리고 서브 클래스를 구현했다
하지만 문제가 아직 남아있다.
1.첨가물이 바뀔때마다 코드를 계속 수정해야 한다
2. 종류가 많아지면 새 메소드를 추가해야 하고 슈퍼클래스에 cost()도 수정해야 한다.
3. 새로운 음료가 추가될 수도 있고, 특정 첨가물이 들어가면 안되는 경우도 있을것이다.
4. 첨가물을 두번 추가하면??
여기서 디자인 원칙!
클래스는 확장에는 열려있지만, 변경에는 닫혀있어야 한다.(OCP)
이를 해결하기 위해 모순적으로 보이지만 코드를 확장하게 해주는 기법이 있습니다.
먼저 DarkRoast라는 객체가 있다
음료수 이름이다
여기에 모카를 추가해보자
여기에 휘핑크림을 추가해보자
이런식으로 객체에 추가요소를 동적으로 더할 수 있다.
모카와 휘핑크림이 데코레이터라고 할 수 있다.
이 데코레이터는 DarkRoast(객체)와 같은 형식이다.
whip의 cost()를 호출하면 mocha의 cost로 가서DarkRoast의 cost를 호출하고 다시 계산하면서 빠져나온다.
이를 UML로 표현하면 다음과 같다
데코레이터와 데코레이터로 감싸는 객체의 형식을 맞추기 위해 Beaverage를 상속 받아 사용한다
상위 추상 클래스
public abstract class Beverage {
String description = "제목없음";
public String getDescription(){
return description;
}
public abstract double cost();
}
꾸밈당할 객체들
public class DarkRoast extends Beverage{
public DarkRoast(){
this.description = "다크로스트";
}
@Override
public double cost() {
return .99;
}
}
public class Espresso extends Beverage{
public Espresso(){
description = "에스프레소";
}
@Override
public double cost() {
return 1.99;
}
}
꾸밀 객체(데코레이터)들 상위 클래스
public abstract class CondimentDecorator extends Beverage{
Beverage beverage; //데코레이터가 감쌀 객체를 지정
public abstract String getDescription();
}
데코레이터들
public class Mocha extends CondimentDecorator{
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost() + .20;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 모카";
}
}
public class Soy extends CondimentDecorator{
public Soy(Beverage beverage){
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost() + .15;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 두유";
}
}
//필요한거 만들어서 계속추가
데코레이터 사용하기
public class StarbuzzCoffee {
public static void main(String args[]){
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + "$" + beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + "$" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription() + "$" + beverage3.cost());
}
}