Updated:

1. 개요

객체에 동적으로 새로운 책임을 추가할 수 있도록 하는 패턴

장식자 패턴(Decorator Pattern)은 구조 패턴(Structural Pattern)의 한 종류로, 서브클래스를 구현하지 않고도, 기존의 클래스에 동적으로 새로운 기능을 추가할 수 있도록 하는 패턴이다. 기존의 클래스를 둘러싸서 사용자의 요청을 가로채고, 추가적인 작업을 한 후 기존 클래스로 전달해준다.

2. 구조 패턴(Structural Pattern)

2-1. 구조 패턴이란?

구조 패턴이란 작은 클래스들의 상속과 합성을 통해 더 큰 클래스를 생성하는 방법을 제공하는 패턴으로, 독립적으로 개발한 클래스들을 마치 하나인 것 처럼 사용할 수 있다.

2-2. 구조 패턴 종류

3. 구성

[출처 : GoF의 디자인 패턴]

  • Component : 공통의 기능을 정의한 인터페이스

  • ConcreteComponent : Component 인터페이스 구현 클래스

  • Decorator : Component 인터페이스를 만족하면서 추가할 기능을 정의한 추상 클래스

  • ConcreteDecorator : Component에 새롭게 추가할 기능을 구현한 클래스

4. 예제 코드

케이크를 만든다고 가정해보자. 기본적으로 빵을 굽고 생크림을 바르면 케이크가 완성되고, 딸기나 블루베리를 추가할 수도 있다. 딸기나 블루베리를 추가할 때 Decorator Pattern을 활용할 수 있다.

4-1. Component

[Cake.java]

1
2
3
4
public interface Cake {
    
    void makeCake();
}

4-2. ConcreteComponent

[DefaultCake.java]

1
2
3
4
5
6
7
8
public class DefaultCake implements Cake {

    @Override
    public void makeCake() {
        System.out.println("Baking");
        System.out.println("Add cream");
    }
}

4-3. Decorator

[CakeDecorator.java]

1
2
3
4
5
6
7
8
abstract class CakeDecorator implements Cake {

    protected Cake decoratedCake;

    public CakeDecorator(Cake decoratedCake) {
        this.decoratedCake = decoratedCake;
    }
}

4-4. ConcreteDecorator

[BlueberryCake.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BlueberryCake extends CakeDecorator {

    public BlueberryCake(Cake decoratedCake) {
        super(decoratedCake);
    }

    @Override
    public void makeCake() {
        decoratedCake.makeCake();
        addBlueberry();
    }

    public void addBlueberry() {
        System.out.println("Add blueberry");
    }
}

Line 9 : 기존에 정의된 기능 수행

Line 10 : 새로운 기능 추가

[StrawberryCake.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class StrawberryCake extends CakeDecorator {

    public StrawberryCake(Cake decoratedCake) {
        super(decoratedCake);
    }

    @Override
    public void makeCake() {
        decoratedCake.makeCake();
        addStrawberry();
    }

    public void addStrawberry() {
        System.out.println("Add strawberry");
    }
}

Line 9 : 기존에 정의된 기능 수행

Line 10 : 새로운 기능 추가

5. 테스트

[Clinet.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {

    public static void main(String[] args) {
        Cake cake = new DefaultCake();
        cake.makeCake();
        System.out.println("----------");

        Cake blueberryCake = new BlueberryCake(new DefaultCake());
        blueberryCake.makeCake();
        System.out.println("----------");

        Cake strawberryCake = new StrawberryCake(new DefaultCake());
        strawberryCake.makeCake();
    }
}

[실행 결과]

6. 특징

  • 유연한 설계 : 새로운 기능의 추가/삭제가 런타임 시점에 가능

  • 객체 수의 증가 : 작은 단위의 객체들이 계속 생성되어 객체 수정의 과정이 복잡해짐

7. Composite vs Decorator vs Proxy

  • Composite Pattern

    • 관련된 객체들을 하나의 인터페이스로 관리
  • Decorator Pattern

    • 상속 없이 동적으로 새로운 기능 추가
  • Proxy Pattern

    • 기능을 제공하는 대상에 대한 참조를 직접 관리하지 않음

Updated:

Leave a comment