[디자인패턴 수업 8주차 1차시] Decorator Pattern

Jin Hur·2021년 10월 14일
0
post-custom-banner

Intro: '파일 계층구조', 클래스 다이어그램으로 표현

class File {
    ...
}

class Directory : public File {
    vector<File> files;
    ...
}

Intro: '패키지', 클래스 다이어그램으로 표현

class Present {
    ...
}

class Packing : public Present {
    vector<Present> presents;
    ...
}

데코레이터 패턴

  • 객체의 결합을 통해 기능을 동적으로 유연하게 확장
  • 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우 각 추가 기능을 Decorator 클래스로 정의.
    => Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 방식.
    => 이 패턴을 통해 필요 추가 기능의 조합을 동적으로 생성할 수 있다.

  • Component 클래스
    : 기본 기능을 뜻하는 ConcreteComponent와 추가 기능을 뜻하는 Decorator의 공통 기능 정의. 즉 클라이언트는 Component를 통해 실제 객체를 사용함

  • ConcreteComponent 클래스
    : 기본 기능을 구현하는 클래스

  • Decorator 클래스
    : 많은 수가 존재하는 구체적인 Decorator의 공통 기능을 제공

  • ConcreteDecoratorA, ConcreteDecoratorB 클래스
    : Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻함
    : ConcreteDecorator는 ConcreteComponent 객체에 대한 참조가 필요함.
    (이는 Decorator 클래스에서 Component 클래스로의 합성(composition) 관계를 통해 표현)
    (합성 관계: 생성자에서 필드에 대한 객체를 생성하는 경우)


도로 표시 기능 예

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

public class RoadDisplay {
    public void draw(){
        System.out.println("기본 도로 표시");
    }
}
public class RoadDisplayWithLane extends RoadDisplay{

    @Override
    public void draw() {
        super.draw();
        drawLine();
    }

    // 추가 기능
    private void drawLine(){
        System.out.println("+ 차선 표시");
    }
}
public class Client {
    public static void main(String[] args) {
        // 기본 도로 표시
        RoadDisplay basicDisplay = new RoadDisplay();
        basicDisplay.draw();

        // 차선 표기 기능 추가
        RoadDisplay displayWithLine = new RoadDisplayWithLane();
        displayWithLine.draw();
    }
}

새로운 추가 기능

public class RoadDisplayWithTraffic extends RoadDisplay {
   public void draw() {
     super.draw(); 
     drawTraffic(); // 추가 기능(교통량 표시)
     
   private void drawTraffic() { System.out.println("교통량 표시"); }
}

위와 같이 추가의 기능을 추가해보자.
추가 기능 사용 여부를 포함하여 조합은 4가지 이다.

그렇다면 만약 3가지의 추가 기능이 존재한다면 어떻게 될까?
총 8가지의 경우의 수가 생긴다. 이들을 모두 하나하나의 클래스로 정의한다는 것은 성가신 일이 아닐 수 없다. 다시 말해 다양한 기능의 조합을 고려해야 하는 경우 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야만 한다는 단점이 있다는 것이다.


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


해결책: Decorator Pattern 적용

  • 각 추가 기능별로 개별적인 클래스를 설계하고 기능을 조합할 때 각 클래스의 객체 조합을 이용한다.

public abstract class Display {
    public abstract void draw();
}
// 기본 기능
public class RoadDisplay extends Display{

    @Override
    public void draw() {
        System.out.println("기본 도로 표시");
    }
}
// 추가 기능을 위한 데코레이터 클래스
public class DisplayDecorator extends Display{
    // 합성 관계(강한 집합): RoadDisplay 객체 참조, 이 객체는 공유될 수 없다. 
    private Display decoratedDisplay;
    public DisplayDecorator(Display decoratedDisplay){
        this.decoratedDisplay = decoratedDisplay;
    }

    @Override
    public void draw() {
        decoratedDisplay.draw();
    }
}
// 치선 표시 추가 기능
public class LaneDecorator extends DisplayDecorator{

    // 기존 표시 클래스의 설정
    public LaneDecorator(Display decoratedDisplay){
        super(decoratedDisplay);
    }

    @Override
    public void draw() {
        super.draw();
        // 추가 기능 포함
        drawLine();
    }

    // 추가 기능
    private void drawLine(){
        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 교통량 표시");
    }
}

main

public class Client {
    public static void main(String[] args) {
        // 기본 표시
        Display basicDisplay = new RoadDisplay();
        basicDisplay.draw();

        // 차선 표시 기능                            // 기본 기능
        Display roadWithLane = new LaneDecorator(new RoadDisplay()); // 합성 관계
        roadWithLane.draw();

        // 교통량 표시 기능
        Display roadWithTraffic = new TrafficDecorator(new RoadDisplay());
        roadWithTraffic.draw();

    }
}

main2


이러한 데커레이터 패턴을 바탕으로 OCP를 준수하면서 중복되는 코드를 없앨 수 있었다.
main2 클라이언트 코드 보면, '교통량 표시', '차선 표시' 기능을 제공하는 클래스를 그저 상속 관계로만 구현하려면 기존에 있던 '교통량 표시 클래스', '차선 표시 클래스'에 존재하던 코드를 똑같이 중복하여 작성하는 것과 같다.
이러한 데커레이터 패턴을 사용하여 단지 런타임 중 기능을 합성하는 데커레이션 방식을 활용하여 손쉽게 다양한 기능을 수행하는 객체를 생성할 수 있다.

post-custom-banner

0개의 댓글