티스토리 뷰
반응형
데코레이터 패턴?
기본 기능에 추가할 수 있는 기능의 종류가 많은 경우, 필요한 기능들만을 골라 조합하여 설계하는 디자인 패턴
- ex) 기본 도로 표시 기능 (+ 차선 표시, 교통량 표시, 교차로 표시, 단속 카메라 표시)
- 4가지 추가 기능이 있을 때, 이 들의 조합은 총 15가지가 된다.
- 데코레이터 패턴을 사용하면 이를 4가지 Decorator 클래스만 구현하여 객체 형태로 조합하여 추가 기능의 조합을 구현할 수 있다.
등장 배경
네비게이션에 아래와 같은 기능이 있다고 해보자. 그리고 이를 클래스 다이어그램과 코드로 표현하면 다음과 같다.
- 기본 기능 : 도로를 간단한 선으로 표시
- 추가 기능 : 도로의 차선을 표시하는 기능
// 기본 도로 표시 클래스
public class RoadDisplay {
public void draw() {
System.out.println("기본 도로 표시");
}
}
// 기본 도로 표시 + 차선 표시 클래스
public class RoadDisplayWithLane extends RoadDisplay {
public void draw() {
super.draw();
drawLane();
}
private void drawLane() {
System.out.println("차선 표시");
}
}
public class Client {
public static void Main(String[] args) {
RoadDisplay road = new RoadDisplay();
road.draw(); // 기본 도로만 표시
RoadDisplayWithLane roadWithLane = new RoadDisplayWithLane();
roadWithLane.draw(); // 기본 도로 + 차선 표시
}
}
문제점
(1) 또 다른 도로 표시 기능을 추가할 경우
교통량을 표시하는 클래스를 만들기 위해 RoadDisplayWithLane 클래스와 마찬가지로 RoadDisplay 클래스를 상속받아 RoadDisplayWithTraffic 클래스를 만든다.
// 기본 도로 표시 + 교통량 표시 클래스
public class RoadDisplayWithTraffic extends RoadDisplay {
public void draw() {
super.draw();
drawTraffic();
}
private void drawTraffic() {
System.out.println("교통량 표시");
}
}
(2) 추가 기능을 조합해야 하는 경우
기본 기능 클래스의 하위 클래스로 추가 기능을 만드는 것은 적절할 수도 있지만, 기능의 다양한 조합을 고려할 경우에 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야 한다는 단점이 있다.
기본 기능에 추가로 사용할 수 있는 기능이 3가지 (차선 표시, 교통량 표시, 교차로 표시) 가 있다고 했을 때, 아래 표와 같이 8가지 조합이 가능하다.
이를 상속을 통해 설계한다면 각 조합별로 하위 클래스를 구현해야 한다. 아래는 추가 기능의 조합을 설계한 모습이다.
해결책
상속을 이용한 기능 추가 방법을 설명했지만, 이 방법은 추가 기능이 늘어날때마다 기능의 조합별로 하위 클래스를 구현해야 한다.
조합 수가 늘어나는 문제를 해결하기 위해서는 각 추가 기능별로 개별 클래스를 설계하고, 기능을 조합할 때 각 클래스의 객체 조합을 사용한다.
public abstract class Display {
public abstract void draw();
}
// 기본 도로 표시 클래스
public class RoadDisplay extends Display {
public void draw() {
System.out.println("기본 도로 표시");
}
}
// 다양한 추가 기능에 대한 공통 클래스
public abstract class DisplayDecorator extends Display {
private Display decoratedDisplay;
public DisplayDecorator(Display decoratedDisplay) {
this.decoratedDisplay = decoratedDisplay;
}
public void draw() {
this.decoratedDisplay.draw();
}
}
// 차선 표시 클래스
public class LaneDecorator extends DisplayDecorator {
public LaneDecorator(Display displayDecorator) {
super(displayDecorator);
}
public void draw() {
super.draw();
drawLane();
}
private void drawLane() {
System.out.println("\t차선 표시");
}
}
// 교통량 표시 클래스
public class TrafficDecorator extends DisplayDecorator {
public TrafficDecorator(Display displayDecorator) {
super(displayDecorator);
}
public void draw() {
super.draw();
drawTraffic();
}
private void drawTraffic() {
System.out.println("\t교통량 표시");
}
}
- 도로를 표시하는 기본 기능만 필요하다면 RoadDisplay 객체만을 이용하면 된다.
- 차선 표시 기능이 필요하다면 RoadDisplay + LandDecorator
- 교통량 표시 기능이 필요하다면 RoadDisplay + TrafficDecorator
public class Client {
public static void main(String[] args) {
Display road = new RoadDisplay();
road.draw(); // 기본 도로 표시
Display roadWithLane = new RoadDisplay(new LaneDecorator());
roadWithLane.draw(); // 기본 도로 표시 + 차선 표시
Display roadWithTraffic = new RoadDisplay(new TrafficDecorator());
roadWithTraffic.draw(); // 기본 도로 표시 + 교통량 표시
}
}
- road, roadWithLane, roadWithTraffic 객체의 접근 모두 Display 클래스를 통해 이루어진다.
- 즉, 어떠한 기능을 추가하던 상관 없이 Client 클래스는 Display 클래스만을 이용하여 일관성 있는 방식으로 도로 정보를 표시할 수 있다.
교차로를 표시하는 기능을 추가해보자.
// 교차로 표시 클래스
public class CrossingDecorator extends DisplayDecorator {
public CrossingDecorator(Display displayDecorator) {
super(displayDecorator);
}
public void draw() {
super.draw();
drawCrossing();
}
private void drawCrossing() {
System.out.println("\t교차로 표시");
}
}
public class Client {
public static void main(String[] args) {
Display roadWithLaneTrafficCrossing = new RoadDisplay(
new LaneDecorator(new TrafficDecorator(new CrossingDecorator()))
);
roadWithLaneTrafficCrossing.draw(); // 기본 도로 표시 + 차선 표시 + 교통량 표시 + 교차로 표시
}
}
Reference
- 도서 'UML과 GoF 디자인 패턴 핵심 10가지로 배우는 JAVA 객체 지향 디자인 패턴'
반응형
'디자인 패턴' 카테고리의 다른 글
Factory Method Pattern (0) | 2021.12.04 |
---|---|
Singleton Pattern (0) | 2021.10.02 |
Template Method Pattern (0) | 2021.09.26 |
Strategy Pattern (0) | 2021.09.15 |
댓글