Home Decorator Pattern
Post
Cancel

Decorator Pattern

✏️ Decorator Pattern (데코레이터 패턴) 이란?

기본 기능에 추가할 수 있는 기능의 종류가 많은 경우, 각 추가 기능을 Decorator 클래스로 정의한 후 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 방식 - 위키백과

객체에 추가적인 기능을 서브 클래스를 생성하는 방식보다 훨씬 유연하게 동적으로 객체를 결합하며 추가할 수 있는 패턴이다.

🚘 도로를 표시하는 네비게이션 SW 만들기

네비게이션의 기능은 다음과 같이 2가지의 기능이 있다.

  • 도로를 간단한 선으로 표시
  • 도로의 차선을 표시

UML

기본적으로 이 네비게이션은 도로를 간단한 선으로 표시하고, 차선은 사용자가 선택하여 기능을 보여줄 수 있다. 기본적으로 보여주는 도로와 추가로 차선을 표시하여 제공하고 싶다면 RoadDisplay 클래스와 이를 상속받는 RoadDisplayWithLane 클래스를 설계해야 할 것이다.

1
2
3
4
5
public class RoadDisplay {
  public void draw() {
    System.out.println("Default Road Display");
  }
}
1
2
3
4
5
6
7
8
9
10
public class RoadWithLaneDisplay extends RoadDisplay {
  public void draw() {
    super.draw();
    drawLane();
  }

  private void drawLane() {
    System.out.println("\tLane Display");
  }
}
1
2
3
4
5
6
7
public static void main(String[] args) {
  RoadDisplay roadDisplay = new RoadDisplay();
  roadDisplay.draw();

  RoadDisplay roadWithLaneDisplay = new RoadWithLaneDisplay();
  roadWithLaneDisplay.draw();
}

RoadDisplay 클래스는 기본 도로 표시 기능만 제공하도록 하고, RoadDisplay 클래스를 상속받는 RoadWithLaneDisplay 클래스는 기본 도로 표시 기능에 추가적으로 차선 표시 기능을 더하기 위해 draw 메소드를 오버라이드하여 구현한다.

1
2
3
Default Road Display
Default Road Display
        Lane Display

💣 문제점

만약 도로의 차선 표시 기능 뿐만 아니라, 교통량과 교차로를 표시하는 기능을 제공하고, 이 기능을 동시에 보여주는 즉, 조합하기 위해선 다음과 같이 수많은 하위 클래스를 설계하고 추가적인 메소드들을 작성해야 할 것이다.

UML

1
2
3
4
public class RoadWithLaneDisplay extends RoadDisplay() { ... }
public class RoadWithTrafficDisplay extends RoadDisplay() { ... }
public class RoadWithTrafficAndLaneDisplay extends RoadDisplay() { ... }
...

💡 해결법

이렇게 조합별로 클래스가 늘어나는 문제를 해결하기 위해 우리는 데코레이터 패턴을 적용하여 해결할 수 있다. 기본 기능인 RoadDisplay 객체에 부가적인 기능(Decorator)인 Traffic , Lane 들을 장식(Decorate)하여 해결해보자.

(UML)

각 부가적인 기능(Decorator) 클래스에서는 기본 기능인 RoadDisplay 클래스가 draw 메소드를 호출하도록 하고, 데코레이터들이 가지고 있는 기능은 직접 제공하도록 한다.

1
2
3
public abstract class Display {
  public abstract void draw();
}

기능을 표시하는 Display 추상 클래스를 작성하여 draw 메소드를 서브 클래스에서 작성하도록 한다.


1
2
3
4
5
public class RoadDisplay extends Display {
  public void draw() {
    System.out.println("Road Display");
  }
}

기본 기능인 도로 표시 기능을 구현한다. draw 메소드는 Display 추상 클래스에서 상속 받아 기능을 구현한다.


1
2
3
4
5
6
7
8
9
10
11
public abstract class DisplayDecorator extends Display {
  private Display decoratedDisplay;

  public DisplayDecorator(Display decoratedDisplay) {
    this.decoratedDisplay = decoratedDisplay;
  };

  public void draw() {
    decoratedDisplay.draw();
  };
}

Decorator의 공통 기능을 제공하는 DisplayDecorator 클래스를 작성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class LaneDecorator extends DisplayDecorator {
  public LaneDecorator(Display decoratedDisplay) {
    super(decoratedDisplay);
  }

  public void draw() {
    super.draw();
    drawLane();
  }

  private void drawLane() {
    System.out.println("\tLane Display");
  }
}

public class TrafficDecorator extends DisplayDecorator {
  public TrafficDecorator(Display decoratedDisplay) {
    super(decoratedDisplay);
  }

  public void draw() {
    super.draw();
    drawLane();
  }

  private void drawLane() {
    System.out.println("\tTraffic Display");
  }
}

public class CameraDecorator extends DisplayDecorator { ... }

LaneDecorator, TrafficDecorator, CameraDecorator 클래스는 실제로 기본 기능에 추가적으로 장식해줄 Decorator 이므로, DisplayDecorator 추상 클래스를 상속 받도록 한다. DisplayDecorator 에서 상속받은 draw() 메소드를 오버라이드하여, 각 부가 기능(Decorator)이 수행해야 할 기능만 추가로 작성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main{
  public static void main(String[] args) {
    Display roadDisplay = new RoadDisplay();
    roadDisplay.draw();

    Display roadWithLaneDisplay = new LaneDecorator(new RoadDisplay());
    roadWithLaneDisplay.draw();

    Display roadWithTrafficDisplay = new TrafficDecorator(new RoadDisplay());
    roadWithTrafficDisplay.draw();

    Display roadWithLaneAndTrafficDisplay = new RoadDisplay();
    roadWithLaneAndTrafficDisplay = new TrafficDecorator(roadWithLaneAndTrafficDisplay);
    roadWithLaneAndTrafficDisplay = new LaneDecorator(roadWithLaneAndTrafficDisplay);
    roadWithLaneAndTrafficDisplay.draw();
  }
}

각 인스턴스 객체는 모두 Display 클래스를 통해 생성되고 있다. Main 클래스는 동일한 Display 클래스를 통해 일관성 있는 방식으로 기능을 제공할 수 있다. 이렇게 데코레이터 패턴을 통해 기능이 추가 될 때마다, 수많은 하위 클래스를 생성하지 않고도, 각 데코레이터 별로 조합하여 설계할 수 있게 되었다.

This post is licensed under CC BY 4.0 by the author.