Priv's Blog

10. 데커레이터 패턴 본문

Dev. Study Note/Design Pattern

10. 데커레이터 패턴

Priv 2022. 7. 3. 23:12


 

 

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개를 구현하고, 개별 추가 기능을 필요에 따라 객체 형태로 조합하면 된다.

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

데커레이터 패턴을 도로 표시 예제에 적용하면 다음과 같다.

 


 


수고하셨습니다!


'Dev. Study Note > Design Pattern' 카테고리의 다른 글

12. 팩토리 메서드 패턴  (0) 2022.07.03
11. 템플릿 메서드 패턴  (0) 2022.07.03
9. 옵서버 패턴  (0) 2022.07.03
8. 커맨드 패턴  (0) 2022.07.03
7. 스테이트 패턴  (0) 2022.07.03
Comments