기존에 구현되어 있는 클래스에 필요한 기능을 추가해 나가는 설계 패턴으로 기능 확장이 필요할 때 객체 간의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 해주어 상속의 대안으로 사용하는 디자인 패턴이다.
Decorator(장식자)라는 이름에서 알 수 있듯이 마치 기본 제품에 포장지나 외부 디자인을 살짝 변경해 줌으로써 새로운 기능을 부여하는 것과 같이, 객체 지향 프로그래밍에서 원본 객체에 대해서 무언가를 장식하여 더 멋진 기능을 가지게 만들수 있게 해준다.
데코레이터 패턴을 이용하면 필요한 추가 기능의 조합을 런타임에서 동적으로 생성할 수 있다. 데코레이터할 대상 객체를 새로운 행동들을 포함한 특수 장식자 객체에 넣어서 행동들을 해당 장식자 객체마다 연결시켜, 서브클래스로 구성할때 보다 훨씬 유연하게 기능을 확장 할 수 있다. 그리고 기능을 구현하는 클래스들을 분리함으로써 수정이 용이해진다.
- Component(Interface) : 원본 객체와 장식된 객체 모두를 묶는 역할
- ConcreteComponent : 원본 객체 (데코레이팅 할 객체)
- Decorator : 추상화된 장식자 클래스
원본 객체를 합성(composition)한 wrappee 필드와 인터페이스의 구현 메소드를 가지고 있다- ConcreteDecorator : 구체적인 장식자 클래스
부모 클래스가 감싸고 있는 하나의 Component를 호출하면서 호출 전/후로 부가적인 로직을 추가할 수 있다.
여기서 Component는 기본 기능을 담당하는 ConcreteComponent와 추가 기능을 담당하는 Decorator의 공통 기능을 정의한다.
ConcreateDecorator은 Decorator의 하위 클래스로, Component의 기본 기능과 Decorator의 추가 기능을 모두 제공한다. 그래서 ConcreateDecorator는 ConcreteComponent에 대한 참조가 필요한데, 조합을 통해서 표현한다.
// Weapon.java (Component) => interface로 구현
public interface Weapon {
// operation()
void fire();
}
// BasicWeapon.java (ConcreteComponent)
public class BasicWeapon implements Weapon {
// 부모객체의 operation 상속받아 구현
@Override
public void fire() {
System.out.println("[ConcreteWeapon] Opening fire...");
}
}
// WeaponAccessory.java (Decorator)
public class WeaponAccessory implements Weapon {
// wrappee 필드 (원본 객체)
private Weapon weapon;
public WeaponAccessory(Weapon weapon) {
this.weapon = weapon;
}
@Override
public void fire() {
weapon.fire();
}
}
// Scope.java (ConcreteDecorator)
public class Scope extends WeaponAccessory {
public Scope(Weapon weapon) {
super(weapon);
}
@Override
public void fire() {
aim();
super.fire();
}
public void aim() {
System.out.println("[Scope] Target aimed...");
}
}
// ButtStock.java (ConcreeteDecorator)
public class ButtStock extends WeaponAccessory {
public ButtStock(Weapon weapon) {
super(weapon);
}
@Override
public void fire() {
shoulder();
super.fire();
}
public void shoulder() {
System.out.println("[ButtStock] Weapon shouldered...");
}
}
// Silencer.java (ConcreteDecorator)
public class Silencer extends WeaponAccessory {
public Silencer(Weapon weapon) {
super(weapon);
}
@Override
public void fire() {
silence();
super.fire();
}
public void silence() {
System.out.println("[Silencer] Weapon silenced...");
}
}
데코레이터 순서는 원본 대상 객체 생성자를 장식자 생성자가 래핑(wrapping) 하는 형태로 생성하여 (ex) new 장식자( new 원본() )) 원본에 장식자를 추가하여 장식(Decorate)하는 형태라고 할 수 있다. 이러한 방식을 사용해 런타임에서 유연하게 객체의 기능들을 수정하고 조합하는데 유용하게 사용될 수 있다.
// Client.java
public class Client {
public static void main(String[] args) {
Weapon weapon = new ButtStock(new BasicWeapon());
System.out.println("----- firing weapon 1 -----");
weapon.fire();
weapon = new Silencer(new Scope(new BasicWeapon()));
System.out.println("----- firing weapon 2 -----");
weapon.fire();
}
}
// 실행결과
----- firing weapon 1 -----
[ButtStock] Weapon shouldered...
[ConcreteWeapon] Opening fire...
----- firing weapon 2 -----
[Silencer] Weapon silenced...
[Scope] Target aimed...
[ConcreteWeapon] Opening fire...