데코레이터 패턴은 객체의 결합 을 통해 기능을 동적으로 유연하게 확장 할 수 있게 해주는 패턴으로, 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의 한 후 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계 하는 방식이다.
요리를 하는 Cook 인터페이스가 있다고 하자.
interface Cook {
public void cooking();
}
고기 요리를 하는 클래스를 만든다.
class MeetCook implements Cook {
public void cooking() {
System.out.println("고기를 굽습니다.");
}
}
소금을 뿌린 고기요리를 하는 클래스를 만들어야 하는경우,
기존 코드를 확장하기 위한 가장 쉬운 방법으로 상속을 활용한다.
class SaltedMeetCook extends MeetCook {
public void cooking() {
super.cooking();
System.out.println("소금을 뿌립니다.");
}
}
이와 같은 방법으로 확장을 할 경우 고객의 취향에 따라 소금을 두번, 세번, 그 이상 뿌려야 한다거나 후추를 뿌리거나 기름을 둘러야 하는 경우가 생긴다면
그 조합의 경우의 수만큼 클래스가 추가되어야 한다.
상속이 아닌 합성을 통하면 보다 유연한 설계가 가능하다.
class SaltDecorator implements Cook {
private Cook cook;
public SaltDecorator(Cook cook) { this.cook = cook }
public void cooking() {
cook.cooking();
System.out.println("소금을 뿌립니다.");
}
}
Cook cook = new SaltDecorator(new MeetCook());
cook.cooking(); // 고기를 굽습니다, 소금을 뿌립니다.
하지만 추가적인 데코레이터가 필요한 경우, 중복 코드가 발생한다.
class PepperDecorator implements Cook {
private Cook cook; // 중복!
public PepperDecorator(Cook cook) { this.cook = cook } // 중복!
public void cooking() {
cook.cooking();
System.out.println("후추를 뿌립니다.");
}
}
합성을 사용하여 설계를 유연하게 하고, 추가 기능을 구현하는 클래스들의 중복을 추상화하여 구현한 패턴이 데코레이터 패턴이다.
Cook 클래스를 장식할 데코레이터의 추상 클래스를 만든다.
abstract class CookDecorator implements Cook {
private Cook cook;
public CookDecorator(Cook cook) {
this.cook = cook;
}
public void cooking() {
cook.cooking();
}
}
필요한 데코레이터의 클래스들을 만든다.
class SaltDecorator extends CookDecorator {
public SaltDecorator(Cook cook) { super(cook); }
@Override
public void cooking() {
super.cooking();
System.out.println("소금을 뿌립니다.");
}
}
class PepperDecorator extends CookDecorator {
public PepperDecorator(Cook cook) { super(cook); }
@Override
public void cooking() {
super.cooking();
System.out.println("후추를 뿌립니다.");
}
}
아래와 같이 추가적인 클래스 없이 필요에 따라 다양한 조합을 만들어낼 수 있다.
Cook saltedMeet = new SaltDecorator(new MeetCook());
saltedMeet.cooking(); // 고기를 굽습니다, 소금을 뿌립니다
Cook saltedAndPepperMeet = new SaltDecorator(new PepperDecorator(new MeetCook()));
// 고기를 굽습니다, 후추를 뿌립니다, 소금을 뿌립니다