데코레이터 패턴(Decorator Pattern)

박세건·2024년 5월 21일
0

디자인 패턴

목록 보기
9/17
post-thumbnail

데코레이터 패턴이란, 객체에 대한 기능 확장이나 변경이 필요할때 객체의 결합을 통해서 만들어 내는 구조 패턴이다.
런타임 시간에 추가적인 확장이나 변경이 가능하다
'장식자'라는 의미를 갖고있는데 기본 제품에 외부 디자인이나 포장을 변경해 줌으로써 새로운 기능을 부여하는 것, 객체 지향에서는 원본 객체에 무엇인가를 장식해서 더 멋진 기능을 갖게 한다.

구조

  • Component : 장식자 객체와 원본 객체를 하나의 타입으로 묶어주는 인터페이스
  • ConcreteComponent : 원본 객체
  • Decorator : 장식자를 정의하는 추상 클래스
  • ConcreteDecorator : 구체적인 장식자 클래스

사용 시기

  • 상황에 따라 기능이 변화 하는 경우
  • 결합을 통해 기능이 생성되어야 하는 경우
  • 런타임에 추가적인 동작 할당이 있어야하는 경우
  • 동작 확장이 불가능할때

장점

  • 서브 클래스를 만들어서 사용하는 것보다 훨씬 더 유연
  • 여러 데코레이터로 결합가능
  • 런타임에 추가적인 기능 가능
  • 각 작식자 마다 책임을 갖기게 SRP 원칙 준수
  • 기능 확장을 위해 장식자만 추가하면 되기때문에 OCP 준수

단점

  • 중간 지점에 적용된 특정 데코레이터를 제거하는 것이 어렵다
  • 연속적인 결합 코드가 좋지 않을 수 있다
  • 순서에 강하게 의존해야함

예시 코드

// 원본 객체와 장식된 객체 모두를 묶는 인터페이스
interface IComponent {
    void operation();
}

// 장식될 원본 객체
class ConcreteComponent implements IComponent {
    public void operation() {
    }
}

// 장식자 추상 클래스
abstract class Decorator implements IComponent {
    IComponent wrappee; // 원본 객체를 composition

    Decorator(IComponent component) {
        this.wrappee = component;
    }

    public void operation() {
        wrappee.operation(); // 위임
    }
}

// 장식자 클래스
class ComponentDecorator1 extends Decorator {

    ComponentDecorator1(IComponent component) {
        super(component);
    }

    public void operation() {
        super.operation(); // 원본 객체를 상위 클래스의 위임을 통해 실행하고
        extraOperation(); // 장식 클래스만의 메소드를 실행한다.
    }

    void extraOperation() {
    }
}

class ComponentDecorator2 extends Decorator {

    ComponentDecorator2(IComponent component) {
        super(component);
    }

    public void operation() {
        super.operation(); // 원본 객체를 상위 클래스의 위임을 통해 실행하고
        extraOperation(); // 장식 클래스만의 메소드를 실행한다.
    }

    void extraOperation() {
    }
}
public class Client {
    public static void main(String[] args) {
        // 1. 원본 객체 생성
        IComponent obj = new ConcreteComponent();

        // 2. 장식 1 하기
        IComponent deco1 = new ComponentDecorator1(obj);
        deco1.operation(); // 장식된 객체의 장식된 기능 실행

        // 3. 장식 2 하기
        IComponent deco2 = new ComponentDecorator2(obj);
        deco2.operation(); // 장식된 객체의 장식된 기능 실행

        // 4. 장식 1 + 2 하기
        IComponent deco3 = new ComponentDecorator1(new ComponentDecorator2(obj));
    }
}

장식자 클래스와 원본 클래스가 동일한 인터페이스를 상속받기 때문에 추가적으로 덧붙여서 결과값을 반환할 수 있다.

만약 데코레이터 패턴을 사용하지 않는다면 장식이 적용된 클래스를 각각 생성해주어야한다.
Ex.
데코레이터 패턴을 사용하지 않으면, 우유, 설탕, 시럽을 조합한 각각의 경우에 대해 모든 클래스를 생성해야 합니다.
기본 커피 클래스: BasicCoffee
우유가 들어간 커피 클래스: MilkCoffee
설탕이 들어간 커피 클래스: SugarCoffee
우유와 설탕이 들어간 커피 클래스: MilkSugarCoffee
우유와 시럽이 들어간 커피 클래스: MilkSyrupCoffee
설탕과 시럽이 들어간 커피 클래스: SugarSyrupCoffee
우유, 설탕, 시럽이 모두 들어간 커피 클래스: MilkSugarSyrupCoffee
이처럼 모든 조합을 처리하려면 조합 가능한 옵션이 많아질수록 클래스의 수가 기하급수적으로 증가하게 됩니다. 새로운 옵션이 추가될 때마다 더 많은 클래스를 생성해야 하며, 이는 관리와 유지보수를 어렵게 만듭니다.

위와같은 경우에서 각각의 데코레이터 패턴을 사용한다면 우유, 설탕, 시럽에 대한 데코레이터 클래스를 생성해주면 해결된다.

profile
멋있는 사람 - 일단 하자

0개의 댓글