데커레이터 패턴(Decorator Pattern)
- 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 사용하는 패턴이다.
- 데커레이터 패턴은 기본 기능에 추가될 수 있는 많은 수의 부가 기능에 대해서 다양한 조합을 동적으로 구현할 수 있는 패턴이다.
- 동적으로 클래스의 기능을 확장시키고자 할 때 사용하는 패턴 (추가되는 기능을 갖는 클래스만 만들어주면 됨)
도로 표시 방법 조합 프로그램 example
- 도로 표시
- RoadDisplay 클래스: 기본 도로 표시 기능을 제공하는 클래스
- RoadDisplayWithLane 클래스: 기본 도로 표시에 추가적으로 차선을 표시하는 클래스
- 소스 코드
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
public class RoadDisplay { // 기본 도로 표시 클래스
public void draw() {
System.out.println("도로 기본 표시");
}
}
public class RoadDisplayWithLane extends RoadDisplay { // 기본 도로 표시 + 차선 표시 클래스
public void draw() {
super.draw(); // 상위 클래스 즉 RoadDisplay의 draw 메서드를 호출해서 기본 도로를 표시
drawLane();
}
private void drawLane() {
System.out.println("차선 표시");
}
}
public class Client {
public static void main(String[] args) {
RoadDisplay road = new RoadDisplay();
road.draw(); // 기본 도로만 표시
RoadDisplay roadWithLane = new RoadDisplayWithLane();
roadWithLane.draw(); // 기본 도로 + 차선 표시
}
}
문제점
- 1) 또다른 추가적인 도로 표시 기능을 구현하고 싶다면 어떻게 해야 하는가? 예를 들어 기본 도로 표시에 교통량을 표시하고 싶다면?
- 기본 도로 표시에 추가적으로 교통량을 표시하는 경우
1 2 3 4 5 6 7 8 9
public class RoadDisplayWithTraffic extends RoadDisplay { public void draw() { super.draw(); drawTraffic(); } private void drawTraffic() { System.out.println("교통량 표시"); } }
- 기본 도로 표시에 추가적으로 교통량을 표시하는 경우
- 2) 여러가지 추가 기능의 조합하여 제공하고 싶다면 어떻게 해야 하는가? 예를 들어 기본 도료 표시에 차선 표시 기능과 교통량 표시 기능을 함께 제공하고 싶다면?
- 여러 가지 추가 기능을 조합해야 하는 경우 다음과 같아진다.
- RoadDisplay클래스를 상속 받은 클래스가 엄청 많아지게 되는 문제가 발생한다.
- 여러 가지 추가 기능을 조합해야 하는 경우 다음과 같아진다.
해결책
- 추가 기능 별로 개별적인 클래스를 설계하고 이를 조합
- 동적으로 클래스의 기능을 확장 가능하게 됨
- 기본 기능이 여러개 (RoadDisplayA, RoadDisplayB) 있을 수 있으므로 Display를 추상클래스로 만들어 하위클래스로 둔다.
- 하나만 있을시 Decorator가 직접 RoadDisplay와 연관 관계를 맺어도 문제는 없다!
- 소스 코드
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public abstract class Display {
public abstract void draw();
}
public class RoadDisplay extends Display { // 기본 도로 표시 클래스
public void draw() {
System.out.println("도로 기본 표시");
}
}
// 다양한 추가 기능에 대한 공통 클래스
public class DisplayDecorator extends Display {
private Display decoratedDisplay;
public DisplayDecorator(Display decoratedDisplay) {
this.decoratedDisplay = decoratedDisplay;
}
public void draw() {
decoratedDisplay.draw();
}
}
public class LaneDecorator extends DisplayDecorator { // 차선표시를 축하는 클래스
public LaneDecorator(Display decoratedDisplay) { // 기존 표시 클래스의 설정
super(decoratedDisplay);
}
public void draw() {
super.draw(); // 설정된 기존 표시 기능을 수행
drawLane(); // 추가적으로 차선을 표시
}
private void drawLane() { System.out.println("\t차선 표시"); }
}
public class TrafficDecorator extends DisplayDecorator { // 교통량 표시를 추가하는 클래스
public TrafficDecorator(Display decoratedDisplay) { // 기존 표시 클래스의 설정
super(decoratedDisplay);
}
public void draw() {
super.draw(); // 설정된 기존 표시 기능을 수행
drawTraffic(); // 추가적으로 교통량을 표시
}
private void drawTraffic() { System.out.println("\t교통량 표시"); }
}
public class Client {
public static void main(String[] args) {
Display road = new RoadDisplay();
road.draw(); // 기본 도로 표시
Display roadWithLane = new LaneDecorator(new RoadDisplay());
// Client 클래스는 동일한 Display 클래스만을 통해서 일관성 있는 방식으로 도로 정보를 표시
roadWithLane.draw(); // 기본 도로 표시 + 차선 표시
Display roadWithTraffic = new TrafficDecorator(new RoadDisplay());
roadWithTraffic.draw(); // 기본 도로 표시 + 교통량 표시
}
- 실행 결과
- roadWithLane 객체의 draw메서드 동작
- 만약 기본 도로 표시 기능 + 차선 표시 + 교통량 표시를 한 번에 조합해야 한다면?
1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
Display roadWithLaneAndTraffic =
new TrafficDecorator(
new LaneDecorator(
new RoadDisplay()));
roadWithLaneAndTraffic.draw();
}
}
- 교차로 기능을 추가한다면?
- 소스 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CrossingDecorator extends DisplayDecorator {
public CrossingDecorator(Display decoratedDisplay) {
super(decoratedDisplay);
}
public void draw() {
super.draw();
drawCrossing();
}
private void drawCrossing() {
System.out.println("\t횡단보도 표시");
}
}
public class Client {
public static void main(String[] args) {
Display roadWithLaneAndTraffic =
new LaneDecorator(
new TrafficDecorator(
new CrossingDecorator(
new RoadDisplay())));
roadWithCrossingAndTrafficAndLane.draw();
}
}
- 실행 결과