10. 데커레이터 패턴
1. 도로 표시 방법 조회하기
내비게이션에서 도로를 표시하는 기능을 가정하자.
기본적인 기능은 도로를 선으로 표시하는 것이겠지만, 내비게이션의 종류마다 차선을 표시해주기도 한다.
즉, 차선 표시 기능은 핵심/기본 기능이 아니라 추가 기능이다.
RoadDisplay 클래스는 핵심/기본 기능인 도로를 표시하는 기능을 담당한다.
RoadDisplayWithLane 클래스는 차선을 표시하는 추가 기능을 담당한다.
여기서 RoadDisplayWithLane 클래스 역시 도로를 표시하므로, RoadDisplay 클래스의 하위 클래스에 해당한다.
package P10;
public class RoadDisplay {
public void Draw() {
System.out.println("기본 도로 표시");
}
}
package P10;
public class RoadDisplayWithLane extends RoadDisplay {
public void Draw() {
super.Draw();
DrawLine();
}
private void DrawLine() {
System.out.println("차선 표시");
}
}
RoadDisplayWithLane클래스에서 기본 도로 표시 기능은 상위 클래스를 통해 구현하고, 차선 표시 기능은 drawLane 메서드로 구현한다.
2. 문제점
또 다른 도로 표시 기능을 추가로 구현하고 싶다면?
여러 추가 기능을 조합해 제공하고 싶다면?
2.1) 또 다른 도로 표시 기능을 추가할 경우
RoadDisplayWithLane 클래스처럼 새로운 클래스를 정의하여 RoadDisplay의 자식 클래스로 만들면 된다.
package P10;
public class RoadDisplay {
public void Draw() {
System.out.println("기본 도로 표시");
}
}
package P10;
public class RoadDisplayWithTraffic extends RoadDisplay {
public void Draw() {
super.Draw();
DrawTraffic();
}
private void DrawTraffic() {
System.out.println("교통량 표시");
}
}
package P10;
public class RoadDisplayWithLane extends RoadDisplay {
public void Draw() {
super.Draw();
DrawLine();
}
private void DrawLine() {
System.out.println("차선 표시");
}
}
2.2) 여러 추가 기능을 조합해야 하는 경우
다양한 기능의 조합을 고려해야 하는 경우, 상속을 통한 기능 확장은 각 기능별로 클래스를 추가해야 한다는 단점이 있다.
즉, 차선, 교통량, 교차로 등 다양한 기능을 추가로 제공할 경우, 그 모든 경우에 수에 따른 클래스를 만들어야 한다는 것이다.
이 모든 클래스를 구현하는 것은 너무 비효율적이고 복잡한 방식이다.
3. 해결책
상속을 이용하는 방법을 사용하기에는 경우의 수가 기하급수적으로 늘어날 수 있다는 문제점이 존재한다.
이를 해결하기 위해서는 각 추가 기능별로 개별적인 클래스를 설계하고, 기능을 조합할 때 각 클래스의 객체 조합을 사용하면 된다.
즉, 필요에 따라 단일 기능을 제공하는 클래스 여러 개를 합쳐서 복수 기능을 구현하는 것이다.
package P10;
public abstract class Display {
public abstract void Draw();
}
package P10;
public class RoadDisplay extends Display {
public void Draw() {
System.out.println("기본 도로 표시");
}
}
package P10;
public class DisplayDecorator extends Display {
private Display decorDisplay;
public DisplayDecorator(Display decorDisplay) {
this.decorDisplay = decorDisplay;
}
public void Draw() {
decorDisplay.Draw();
}
}
package P10;
public class LaneDecorator extends DisplayDecorator {
public LaneDecorator(Display decorDisplay) {
super(decorDisplay);
}
public void Draw() {
super.Draw();
DrawLine();
}
private void DrawLine() {
System.out.println("차선 표시");
}
}
package P10;
public class TrafficDecorator extends DisplayDecorator {
public TrafficDecorator(DisplayDecorator decorDisplay) {
super(decorDisplay);
}
public void Draw() {
super.Draw();
DrawTraffic();
}
private void DrawTraffic() {
System.out.println("교통량 표시");
}
}
만약 여기에 교차로를 표시하는 추가 기능을 구현하면서 기존의 다른 추가 기능과의 조합을 지원하려면 다음과 같이 구현하면 된다.
4. 데커레이터 패턴
데커레이터 패턴은 기본 기능에 추가할 수 있는 다양한 기능들이 존재할 경우, 각 추가 기능들을 Decorator 클래스로 정의한 후, 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 패턴이다.
기본 도로 표시 기능, 차선 표시, 교통량 표시, 단속 카메라 표시까지 총 추가 기능이 4가지가 있을 때 모든 조합은 15가지나 된다.
이때 데커레이터 패턴을 사용하면 개별 추가 기능에 해당하는 Decorator 4개를 구현하고, 개별 추가 기능을 필요에 따라 객체 형태로 조합하면 된다.
즉, 데커레이터 패턴은 기본 기능에 추가할 수 있는 많은 종류의 부가 기능에서 파생되는 다양한 조합을 동적으로 구현할 수 있는 패턴이다.
데커레이터 패턴을 도로 표시 예제에 적용하면 다음과 같다.