Decorator 패턴

Decorator 패턴

 - 객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있도록 해주는 패턴이다.

 - 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의한 후 필요한

   Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 방식이다.

 - 기본 기능에 추가할 수 있는 많은 종류의 부가 기능에서 파생되는 다양한 조합을 동적으로 구현할 수 있는 패턴이다.

 

 

Component 

 - 기본 기능을 뜻하는 ConcreteComponent와 추가 기능을 뜻하는 Decorator의 공통 기능을 정의한다.

 - 클라이언트는 Component를 통해 실제 객체를 사용한다. 

ConcreteComponent 

 - 기본 기능을 구현하는 클래스 

Decorator 

 - 많은 수가 존재하는 구체적인 Decorator의 공통 기능을 제공한다.

ConcreteDecorator 

 - Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻한다.

 - ConcreteDecorator 클래스는 ConcreteComponent 객체에 대한 참조가 필요한데, 이는 Decorator 클래스에서

   Component 클래스로의 합성(composition) 관계를 통해 표현된다.

 

합성관계  

 - 생성자에서 필드에 대한 객체를 생성하는 경우

 - 전체 객체의 라이프타임과 부분 객체의 라이프 타임은 의존적이다.

 - 전체 객체가 없어지면 부분 객체도 없어진다.

 

예시 

도로 표시 방법 조합하기

도로를 간단한 선으로 표시하는 기능 (기본 기능)

도로의 차선을 표시하는 기능 (추가 기능)

1
2
3
4
5
// 기본 도로 표시 클래스
public class RoadDisplay {
    public void draw() { System.out.println("기본 도로 표시"); }
}
 
cs

 

1
2
3
4
5
6
7
8
9
// 기본 도로 표시 + 차선 표시 클래스
public class RoadDisplayWithLane extends RoadDisplay {
  public void draw() {
      super.draw(); // RoadDisplay 클래스(상위 클래스) 의 draw 메서드를 호출해서 기본 도로 표시
      drawLane(); // 추가 기능인 차선 표시
  }
  private void drawLane() { System.out.println("차선 표시"); }
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
public class Client {
  public static void main(String[] args) {
      RoadDisplay road = new RoadDisplay();
      road.draw(); // 기본 도로만 표시
 
      RoadDisplay roadWithLane = new RoadDisplayWithLane();
      roadWithLane.draw(); // 기본 도로 표시 + 차선 표시
  }
}
 
cs

 - RoadDisplay 클래스에는 기본 도로 표시 기능을 실행하기 위한 draw 메서드를 구현한다.

 - RoadDisplayWithLane 클래스에는 차선 표시 기능을 추가하기 위해 상속받은 draw 메서드를 오버라이드한다.

    - 기본 도로 표시 기능 : 상위 클래스(RoadDisplay)의 draw 메서드 호출 

    - 차선 표시 기능 : 자신의 drawLane 메서드 호출

 

위와 같은 방식은 또 다른 도로 표시 기능을 추가로 구현하는 경우, 여러 가지 추가 기능을 조합해야 하는 경우 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해주어야 한다는 문제점이 발생할 수 있다. 

 

-> 위 문제를 해결하기 위해서는 각 추가 기능별로 개별적인 클래스를 설계하고 기능을 조합할 때 각 클래스의 객체

    조합을 이용하면 된다. 

 

 

 - 도로를 표시하는 기본 기능만 필요한 경우 RoadDisplay 객체를 이용한다.

 - 차선을 표시하는 추가 기능도 필요한 경우 RoadDisplay 객체와 LaneDecorator 객체를 이용한다. 

   - LaneDecorator 클래스에서는 차선 표시 기능만을 직접 제공한다. ( drawLane() ) 

   - 기본 도로 표시 기능은 RoadDisplay 클래스의 draw 메서드를 호출한다. ( super.draw() )

      (DisplayDecorator 클래스에서 Display 클래스로의 합성관계를 통해 RoadDisplay 객체에 대한 참조)

 

Display 클래스

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

 

RoadDisplay 클래스

1
2
3
4
5
6
/* 기본 도로 표시 클래스 */
public class RoadDisplay extends Display {
  @Override
  public void draw() { System.out.println("기본 도로 표시"); }
}
 
cs

 

DisplayDecorator 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 다양한 추가 기능에 대한 공통 클래스 */
public abstract class DisplayDecorator extends Display {
  private Display decoratedDisplay;
 
  // '합성(composition) 관계'를 통해 RoadDisplay 객체에 대한 참조
  public DisplayDecorator(Display decoratedDisplay) {
      this.decoratedDisplay = decoratedDisplay;
  }
 
  @Override
  public void draw() { 
    decoratedDisplay.draw(); 
    }
}
 
cs

 

LaneDecorator, TrafficDecorator 클래스

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
/* 차선 표시를 추가하는 클래스 */
public class LaneDecorator extends DisplayDecorator {
 
  // 기존 표시 클래스의 설정
  public LaneDecorator(Display decoratedDisplay) { 
         super(decoratedDisplay); 
  }
  @Override
  public void draw() {
      super.draw(); // 설정된 기존 표시 기능을 수행
      drawLane(); // 추가적으로 차선을 표시
  }
  // 차선 표시 기능만 직접 제공
  private void drawLane() { 
      System.out.println("\t차선 표시"); 
  }
}
 
/* 교통량 표시를 추가하는 클래스 */
public class TrafficDecorator extends DisplayDecorator {
 
  // 기존 표시 클래스의 설정
  public TrafficDecorator(Display decoratedDisplay) { 
      super(decoratedDisplay); 
  }
  @Override
  public void draw() {
      super.draw(); // 설정된 기존 표시 기능을 수행
      drawTraffic(); // 추가적으로 교통량을 표시
  }
  // 교통량 표시 기능만 직접 제공
  private void drawTraffic() { 
      System.out.println("\t교통량 표시"); 
  }
}
 
cs

 

Main 클래스

1
2
3
4
5
6
7
8
9
10
11
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(); // 기본 도로 표시 + 교통량 표시
  }
}
 
cs

 - 각 road, roadWithLane, roadWithTraffic 객체의 접근이 모두 Display 클래스를 통해 이루어진다.

 - 어떤 기능을 추가하느냐에 관계없이 Client 클래스는 동일한 Display 클래스만을 통해 일관성 있는 방식으로

   도로 정보를 표시할 수 있다.

 - 위와 같이 Decorator 패턴을 이용하면 추가 기능 조합별로 별도의 클래스를 구현하는 대신 각 추가 기능에 해당하는 

    클래스의 객체를 조합해 추가 기능의 조합을 구현할 수 있다.

 - 추가 기능의 수가 많을수록 효과가 큰 설계 패턴이다. 

 

추가 예시

 

기본 도로 표시 + 차선 표시 + 교통량 표시 

1
2
3
4
5
6
7
8
9
10
11
public class Client {
  public static void main(String[] args) {
  // 기본 도로 표시 + 차선 표시 + 교통량 표시
    Display roadWithLaneAndTraffic =
      new TrafficDecorator(
      new LaneDecorator(
      new RoadDisplay()));
    roadWithLaneAndTraffic.draw();
  }
}
 
cs

 - 가장 먼저 생성된 RoadDisplay 객체의 draw 메서드 실행 

 - 첫 번째 추가 기능인 LaneDecorator 클래스의 drawLane 메서드 실행 

 - 두 번째 추가 기능인 TrafficDecorator 클래스의 drawTraffic 메서드 실행 

 

 

참고 

gmlwjd9405.github.io/2018/07/09/decorator-pattern.html

 

[Design Pattern] 데코레이터 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788968480911&orderClick=JAj&OV_REFFER=click.linkprice.com/

 

JAVA 객체지향 디자인 패턴 - 교보문고

『JAVA 객체지향 디자인 패턴』은 자바와 UML을 중심으로 객체지향 이론이 무엇인지를 배운 다음 GoF에서 소개하는 디자인 패턴의 핵심 10가지를 알기 쉽게 소개한다. 실제 디자인 패턴을 익히고

www.kyobobook.co.kr

 

'설계 패턴' 카테고리의 다른 글

Factory Method  (0) 2020.11.18
템플릿 메서드 패턴  (0) 2020.11.12
Strategy 패턴  (0) 2020.10.06
빌더 패턴 (Builder Pattern)  (0) 2020.10.06
SOLID 원칙  (0) 2020.10.05
TAGS.

Comments