어디선가 한 번쯤은 "데코레이션"이라는 말을 들어봤을 것입니다. 케이크 위에 과일이나 장식을 얹는 것, 음료에 가니쉬나 다양한 첨가물을 넣는 것들을 우리는 데코레이션 이라고 합니다. 이처럼 한 객체 위에 기능들을 입혀서 사용 목적에 걸맞는 객체로 만드는 것을 데코레이션 패턴
이라고 합니다. 데코레이션 패턴
은 구조적 패턴에 속합니다.
예를들면, 케이크 빵위에 생크림을 바르면 생크림 케이크, 초코를 바르면 초코 케이크가 되고, 같은 생크림 케이크 라도 과일을 올릴 수도 있고, 초코를 올릴 수도 있죠. 심지어는 같은 장식(기능)이라도 케이크 빵대신 베이글을 넣을 수도 있습니다. 이처럼 각기 다른 베이스를 가지고 장식을 통해 객체를 목적에 맞게 완성시키기 위한 패턴입니다.
커스텀 케이크 가게를 예시로 들며 간단하게 구현해보겠습니다.
우선 케이크 빵의 종류를 결정하는 Cakesheet 클래스 입니다. 빵의 종류에 따라 이름이 달라질 것이므로 추상클래스로 구현했습니다.
package Decorator;
public abstract class Cakesheet {
//케이크의 종류를 설명하는 description
String description = "케이크시트";
public String getDescription() {
return description;
}
//케이크의 가격을 계산할 cost 메소드
public abstract long cost();
}
이번에는 장식을 담은 Decroation 클래스입니다. 케이크의 설명에 장식이름이 추가될 것이므로 Cakesheet를 상속해서 설명 설정 메소드인 getDescription을 공유하고 있습니다.
package Decorator;
public abstract class Decoration extends Cakesheet {
public abstract String getDescription();
}
케이크 빵을 결정하는 객체입니다 시폰과 스폰지 두 종류를 만들었습니다.
package Decorator;
public class Chiffon extends Cakesheet {
//객체 생성자에서 케이크 이름 명시
public Chiffon() {
description = "시폰 케이크";
}
@Override
public long cost() {
return 7000;
}
}
package Decorator;
public class Sponge extends Cakesheet {
public Sponge() {
description = "스폰지 케이크";
}
@Override
public long cost() {
return 5000;
}
}
이번에는 데코레이션 부분입니다. 데코레이션은 휘핑 크림, 초콜릿, 모카 세 종류를 만들었습니다.
package Decorator;
public class WhippingCream extends Decoration {
Cakesheet cakesheet;
//베이스로 설정한 케이크시트를 가져와서 장식하는데 이용합니다.
public WhippingCream(Cakesheet cakesheet) {
this.cakesheet = cakesheet;
}
//케이크 베이스 가격에 장식의 가격을 더합니다.
@Override
public long cost() {
return cakesheet.cost() + 1000;
}
//케이크 이름에 장식 이름을 더해줍니다.
@Override
public String getDescription() {
return "생크림 " + cakesheet.getDescription();
}
}
package Decorator;
public class Chocolate extends Decoration {
Cakesheet cakesheet;
public Chocolate(Cakesheet cakesheet) {
this.cakesheet = cakesheet;
}
@Override
public long cost() {
return cakesheet.cost() + 1500;
}
@Override
public String getDescription() {
return "초콜릿 " + cakesheet.getDescription();
}
}
package Decorator;
public class Mocha extends Decoration {
Cakesheet cakesheet;
public Mocha(Cakesheet cakesheet) {
this.cakesheet = cakesheet;
}
@Override
public long cost() {
return cakesheet.cost() + 2000;
}
@Override
public String getDescription() {
return "모카 " + cakesheet.getDescription();
}
}
케이크 가게 클래스입니다. 케이크 가게 클래스에 main 메소드가 들어있습니다.
package Decorator;
public class CustomCakeShop {
public static void main(String[] args) {
System.out.println("==1번 손님==");
Cakesheet cake1 = new Sponge();
System.out.println(cake1.getDescription() + "는 " + cake1.cost() + "원");
System.out.println("----------------");
System.out.println("==2번 손님==");
Cakesheet cake2 = new Sponge();
cake2 = new WhippingCream(cake2);
System.out.println(cake2.getDescription() + "는 " + cake2.cost() + "원");
System.out.println("----------------");
System.out.println("==3번 손님==");
Cakesheet cake3 = new Sponge();
cake3 = new WhippingCream(cake3);
cake3 = new Mocha(cake3);
System.out.println(cake3.getDescription() + "는 " + cake3.cost() + "원");
System.out.println("----------------");
System.out.println("==4번 손님==");
Cakesheet cake4 = new Chiffon();
cake4 = new WhippingCream(cake4);
cake4 = new Mocha(cake4);
System.out.println(cake4.getDescription() + "는 " + cake4.cost() + "원");
System.out.println("----------------");
}
}
위 main 메소드를 실행하면 다음과 같이 나옵니다.
이때 객체 생성과정에서 장식 -> 케이크 시트 순서로 생성하면 어떻게 되는가?하는 의문이 생길 수도 있는데요. 그 부분에서는 장식 클래스들의 생성자에 케이크 시트가 존재해야 하는 조건이 달려있으므로 장식부터 호출하게 된다면 오류를 내게 됩니다.
보셨듯이 같은 객체들을 가지고 다양한 조합의 결과를 나타냈습니다. 지금까지 객체의 확장을 자유롭게 만들어주는 패턴인 데코레이터 패턴이었습니다.