데코레이터의 의미는 장식하다, 꾸미다입니다
따라서 파이썬에서는 데코레이터를 함수 실행 전 후에 사용할 수있게 함수를 꾸며주고합니다.
전혀 다르지만 의미가 비슷하게 사용되는 자바에서는 데코레이터 패턴이 있습니다.
예시를 들어 자동차 네비게이션에서 도로를 표시하는 기능(단속 카메라위치, 속도제한)을 켜두자고 생각합시다.
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("차선 표시");
}
}
RoadDisplay
를 상속받아서 차선 표시기능을 추가 할 수 있습니다.
근데 만약 주위 교통량을 살피기 위한 기능을 추가해본다고 생각해봅시다.
또 RoadDisplay
를 상속하여 새로운 클래스를 만들어야합니다.
이렇게 하다보면 모든 필요한 기능의 모든 조합의 클래스를 생성해야하는 문제를 마주합니다.
이런 불필요한 코드를 없애기위해 데코레이터 패턴이 등장했습니다.
위 속성을 살펴보면 디스플레이에 표시한다라는 것이 공통점입니다.
public abstract class Display {
public abstract void draw();
}
해당 클래스를 추상클래스로 만들어줍니다.
가장 기본적 기능을 명시하는 추상클래스를 Component
라고 명칭합니다.
컴포넌트(Component)란 프로그래밍에 있어 재사용이 가능한 각각의 독립된 모듈을 뜻한다.
그리고 이 Component의 실체를 구현할 것을 명시합니다.
public class RoadDisplay extends Display{
@Override
public void draw() {
System.out.println("기본 도로 표시");
}
}
위의 역할을 ConcreteComponent
명칭합니다.
실제 구현체 역할을 합니다.
ConcreteComponent: 독립된 모듈의 구현체
이제 Decorator 즉, 무언가 꾸미는 역할을 연결해주는 Class를 생성해봅시다.
package decorator;
public class DisplayDecorator extends Display{
private Display decoratedDisplay;
public DisplayDecorator(Display decoratedDisplay) {
this.decoratedDisplay = decoratedDisplay;
}
@Override
public void draw() {
this.decoratedDisplay.draw();
}
}
최상위 Display를 setter로 주입을 받아 받아온 Display(추상화) 정확히 말하면 Display의 구현체를 가져와 함수를 실행합니다.
객체를 주입하고 , 주입된 객체의 함수를 호출하는 것이 핵심입니다.
위 역할을 말 그대로 Decorator
라고 명칭합니다.
이제 사용자가 차선을 표시하고싶다 하는 위 예제를 적용해봅시다.
public class LaneDecorator extends DisplayDecorator{
public LaneDecorator(Display decoratedDisplay) {
super(decoratedDisplay);
}
@Override
public void draw() {
super.draw();
drawLane();
}
private void drawLane() {
System.out.println("차선표시");
}
}
생성자를 상속받는DisplayDecorator
생성자를 호출함으로써 객체를 주입하는 것입니다.
그리고 자신의 draw()를 재정의 함으로써 자신의 로직을 추가하면됩니다.
이런것을 ConcreteDecorator
라고 부릅니다.
자신의 할일을 추가적으로 구현하여 public void draw()
의 super.draw()
의 앞뒤로 함수를 decorate(꾸며주기)하면 됩니다.
왜 이제 Decorate
가 붙었는지 이해가 되시나요!?
클라이언트에서 호출하는 코드를 작성해보면 이렇습니다.
Display roadWithLine = new LaneDecorator(new RoadDisplay());
roadWithLine.draw();
// 기본 도로 표시
// 차선표시
만약 위의 트래픽기능을 추가하고싶으면 어떨까요?
public class TrafficDecorator extends DisplayDecorator{
public TrafficDecorator(Display decoratedDisplay) {
super(decoratedDisplay);
}
@Override
public void draw() {
super.draw();
drawTraffic();
}
private void drawTraffic() {
System.out.println("트래픽그리기");
}
}
위와 같이 ConcreteDecorator
의 역할을 하는 것을 추가해주면됩니다.
그렇다면 총 3가지 기능을 갖는 트래픽을 보여주고, 기본 도로를 표시해주고, 차선을 표시해주는 기능을 호출하려면 다음과 같습니다.
Display trafficRoadWithLine = new TrafficDecorator(new LaneDecorator(new RoadDisplay()));
trafficRoadWithLine.draw();
//기본 도로 표시
//차선표시
//트래픽그리기
이제는 여러가지의 조합의 클래스를 만들어 주지 않아도 Decorator를 통해 코드의 낭비를 줄이고 더 가독성도 늘어난 코드를 사용할수 있습니다.
Component
ConcreteComponent
Decorator
ConcreteDecorator