2021. 1. 19. 13:52ㆍ2021/JOB DA STUDY
데코레이터 패턴이란, 기능을 동적으로 유연하게 확장할 수 있게 해주는 패턴이다.
기본 기능 + "추가 기능"
추가 가능한 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의한 후 필요한 Decorator 객체를 조합해 추가 기능의 조합을 설계하는 방식
EX) 기본 도로 표시 기능 + 차선 표시/교통량 표시/교차로 표시/단속 카메라 표시
=> 추가 기능의 모든 조합 = 13(4+6+2+1)
*** 기본 기능에 추가할 수 있는 많은 종류의 부가 기능에서 파생되는 다양한 조합을 동적으로 구현할 수 있는 패턴 ***
- Component
- 기본기능(ConcreteComponent)와 추가기능(Decorator)의 공통 기능을 정의
- 클라이언트는 Component를 통해 실제 객체를 사용함
- ConcreteComponent
- 기본 기능을 구현하는 클래스
- Decorator
- 많은 수가 존재하는 구체적인 Decorator의 공통 기능을 제공
- ConcreteDecoratorA, ConcreteDecoratorB
- Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻함
- ConcreteDecorator 클래스는 ConcreteComponent 객체에 대한 참조가 필요한데,
이는 Decorator 클래스에서 Component 클래스로의 "합성(Composition)관계"를 통해 표현
합성관계 (Composition Relationship)
- 생성자에서 필드에 대한 객체를 생성하는 경우
- 전체 객체의 라이프 타임과 부분 객체의 라이프 타임은 의존적이다.
- 전체 객체가 없어지면, 부분 객체도 없어진다.
도로 표시 방법 조합
네비게이션 SW에서 도로를 표시하는 기능
"기본 기능=도로를 간단한 선으로 표시하는 기능" + "추가 기능=네비게이션 SW에 따라 도로의 차선을 표시하는 기능"
- 기본 도로 표시 클래스
public class RoadDisplay{
public void draw() { System.out.println("기본 도로 표시"); }
}
- 기본 도로 표시 + 차선 표시 클래스
//상속받은 draw 메서드를 Override
public class RoadDisplayWithLane extends RoadDisplay{
public void draw() {
//상위 클래스 RoadDisplay의 draw 메서드 호출
super.draw();
//추가 = 차선 표시
drawLane();
}
private void drawLane() { System.out.println("차선 표시"); }
}
- Client
public class Client{
public static void main(String[] args){
ReadDisplay road = new RoadDisplay();
//기본기능
road.draw();
RoadDisplay roadWithLane = new RoadDisplayWithLane();
//기본기능 + 추가기능
roadWithLane.draw();
}
}
문제점
- 추가적으로 다른 도로 표시 기능을 추가로 구현하는 경우 - 기본 도로 표시 기능 + 교통량 표시 기능
- 기본 도로 표시 + 교통량 표시 클래스
public class RoadDisplayWithTraffic extends RoadDisplay{
public void draw(){
//상위 클래스 RoadDisplay 의 draw 메서드 호출
super.draw();
//추가적으로 교통량 표시
drawTraffic();
}
private void drawTraffix() {System.out.println("교통량 표시"); }
}
경우의 수 | 기본 기능 (도로 표시) | 차선 표시 (추가) | 교통량 표시 (추가) | 교차로 표시 (추가) | 클래스 이름 |
1 | O | RoadDisplay | |||
2 | O | O | RoadDisplayWith Lane |
||
3 | O | O | RoadDisplayWith Traffic |
||
4 | O | O | RoadDisplayWith Crossing |
||
5 | O | O | O | RoadDisplayWith LaneTraffic |
|
6 | O | O | O | RoadDisplayWith TrafficCrossing |
|
7 | O | O | O | RoadDisplayWith LaneCrossing |
|
8 | O | O | O | O | RoadDisplayWith LaneTrafficCrossing |
***상속을 통해 조합의 각 경우를 설계한다면 각 조합별로 하위 클래스 7개를 구현해야한다***
***다양한 기능의 조합을 고려해야 하는 경우, 상속을 통한 기능의 확장은 각 기능 별로 클래스를 추가해야하는 단점***
해결책
- 각 추가 기능별로 개별적 클래스를 설계하고, 기능을 조합할 때 각 클래스의 객체 조합을 이용
- Display 클래스
public abstract class Display { public abstract void draw(); }
- RoadDisplay 클래스 (기본기능)
public class RoadDisplay extends Display{
@Override
public void draw() { Syatem.out.println("기본 도로 표시"); }
}
- DisplayDecorator 클래스 (추가기능)
public class RoadDisplayDecorator extends Display {
private Display decoratedDisplay;
//합성 관계를 통해 RoadDisplay 객체에 대한 참조
public DisplayDecorator(Display decoratedDisplay){
this.decoratedDisplaay = decoratedDisplay;
}
@Override
public void draw() { decoratedDisplay.draw(); }
}
- LaneDecorator, TrafficDecorator 클래스
//차선 표시 추가 클래스
public class LaneDecorator extends DisplayDecorator{
//기존 표시 클래스 설정
public LaneDecorator(Display decoratedDisplay) { super(decoratedDisplay); }
@Override
public void draw(){
super.draw(); //설정된 기존 표시 기능
drawLane(); // 추가된 차선 표시 기능
}
private void drawLane() {System.out.println("차선 표시"); }
}
//교통량 표시 추가 클래스
public class TrafficDecorator extends DisplayDecorator{
//기존 표시 클래스 설정
public TrafficDecorator(Display decoratedDisplay) { super(decoratedDisplay) }
@Override
public void draw(){
super.draw(); //설정된 기존 표시 기능
drawTraffic(); // 추가된 교통량 표시 기능
}
private void drawTraffic() { System.out.println("교통량 표시"); }
}
- Client
public class Client{
public static void main(String[] args){
Display road = new RoadDisplay();
road.draw();
Display roadWithLane = new LaneDecorator(new RoadDisplay());
roadWithLane.draw();
Display roadWithTraffic = new TrafficDecorator(new RoadDisplay());
roadWithTraffic.draw();
}
}
road, raodWithLane, roadWithTraffic 객체의 접근은 모두 Display 클래스를 통해 이루어진다.
따라서 어떤 기능을 추가하느냐에 관계없이 Client 클래스는 동일한 Display 클래스만을 통해 일관성 있는 방식으로 도로 정보를 표시할 수 있다.
이처럼 Decorator 패턴을 이용하면 추가 기능 조합별로 별도의 클래스를 구현하는 대신
각 추가 기능에 해당하는 클래스의 객체를 조합해 추가 기능의 조합을 구현할 수 있게 된다.
추가
- 기본 도로 표시 + 차선 표시 + 교통량 표시
public class Client{
public static void main(String[] args){
Display roadWithLaneAndTraffic = new TrafficDecorator(new LaneDecorator(new RoadDisplay()));
roadWithLaneAndTraffic.draw();
}
}
- 가장 먼저 생성된 RoadDisplay객체의 draw 메서드 실행
- 첫 번째 기능인 LaneDecorator 클래스의 drawLane 메서드 실행
- 두 번째 기능인 TrafficDecorator 클래스의 drawTraffic 메서드 실행
- 교차로를 표시하는 추가 기능(CrossingDecorator)을 지원하면서, 기존의 다른 추가 기능(차선 표시 + 교통량 표시)과의 조합을 지원
public class CrossingDecorator extends DisplayDecorator {
//기존 표시 클래스 설정
public CrossingDecorator(Display decoratedDisplay) { super(decoratedDisplay); }
@Override
public void draw(){
super.draw(); //설정된 기존 표시 기능을 수행
drawCrossing(); //추가적으로 교차로 표시
}
//교차로 표시 기능만 직접 제공
private void drawCrossing() {System.out.println() {System.out.println("교차로 표시"); }
}
- Client에서의 사용
public class Client{
public static void main(String[] args) {
// (기본 도로 표시 + 차선 표시 + 교통량 표시) + 교차로 표시
Display roadWithCrossingLaneAndTraffic = new LaneDecorator( new TrafficDecorator( new CrossingDecorator( new RoadDisplay())));
roadWithCrossingLaneAndTraffic.draw();
}
}
CrossingDecorator를 DisplayDecorator의 하위 클래스로 설계하는데,
CrossingDecorator의 draw 메서드가 호출되면 우선 상위 클래스 DisplayDecorator의 draw 메서드를 호출한 후 CrossingDecorator의 drawCrossing 메서드를 호출한다.
- 가장 먼저 생성된 RoadDisplay 객체의 draw 메서드가 실행
- 첫 번째 추가 기능인 CrossingDecorator 클래스의 drawCrossing 메서드가 실행
- 두 번째 추가 기능인 TrafficDecorator 클래스의 drawTraffic 메서드가 실행
- 세 번째 추가 기능인 LaneDecorator 클래스의 drawLane 메서드가 실행
'2021 > JOB DA STUDY' 카테고리의 다른 글
디자인패턴_Composite Pattern of Structural Pattern (0) | 2021.01.20 |
---|---|
디자인패턴_Factory Method Pattern of Creational Pattern (0) | 2021.01.19 |
디자인패턴_Observer Pattern of Behavioral Pattern (0) | 2021.01.15 |
디자인패턴_Template Pattern of Behavioral Pattern (0) | 2021.01.15 |
Super (0) | 2021.01.15 |