Design pattern

[Design Pattern] Decorator Pattern

s00ng 2022. 8. 10. 08:43
Decorator Pattern 이란?
기존의 기능에 새로운 기능을 추가하고 싶을 경우, 새로운 기능을 Decorator로 만들어서 추가하는 방식
기본 기능에 추가할 수 있는 기능의 종류가 많은 경우, 각 추가 기능을 Decorator 클래스로 정의한 후 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계

 

Decorator Pattern 컬레보레이션

 

  • Component: 기본 기능을 뜻하는 ConcreteComponent와 추가 기능을 뜻하는 Decorator의 공통 기능을 정의. Client는 Component를 통해 실제 객체 사용
  • ConcreteComponent: 기본 기능을 구현하는 클래스
  • Decorator: 많은 수가 존재하는 구체적인 Decorator 의 공통 기능 제공
  • ConcreteDecoratorA, ConcreteDecoratorB : Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능 구현한 클래스

 

Decorator Pattern 구현

교재 예시: 네비게이션 SW 에서 도로 표시하는 기능
  • 기본 기능 : 도로를 간단한 선으로 표시
  • 추가 기능 : 도로의 차선 표시, 교통량 표시 등등

 

구현방법 )  상속을 통한 기능 확장 (Decorate pattern 사용 x)

  • 기본 도로 표시 클래스 : RoadDisplay
  • 기본 도로 표시 + 차선 표시 클래스 : RoadDisplayWithLane
public class RoadDisplay { // 기본 도로 표시 클래스
	public void draw(){
    	System.out.printnln("기본 도로 표시");
    }
}

public class RoadDisplayWithLane extends RoadDisplay {
	public void draw() {
    	super.draw();
        drawLane();
    }
	
    private void drawLane(){
    	System.out.println("차선 표시");
    }
}

 

문제점

 

1) 또다른 도로 표시 기능을 추가로 구현하고 싶은 경우

→ 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야함

 

 

2) 여러 가지 추가 기능을 조합해 제공하고 싶은 경우

→ 추가되는 기능의 조합별로 하위 클래스를 구현해야하는 문제

  • ex) 기본 도로 표시 기능에 차선 표시, 교통량 표시, 교차로 표시, 단속 카메라 표시의 4가지 추가 기능이 있을 때 추가 기능의 모든 조합은 15가지가 된다. 

 

문제 해결

Decorate pattern을 통한 확장 : 각 추가 기능별로 개별적인 클래스를 설계하고 기능을 조합할 때 각 클래스의 객체 조합을 이용
  • Component 역할: Display 클래스 
  • ConcreteComponent 역할: RoadDisplay 클래스
  • Decorator 역할: DisplayDecorator 클래스 
  • ConcreteDecorator 역할 : LaneDecorator 클래스, TrafficDecorator 클래스 
public class Display {
	public abstract void draw();
}

public class RoadDisplay extends Display { // 기본 도로 표시 클래스
	public void draw() {
     	System.out.println("기본 도로 표시");
    }
}

public abstract class DisplayDecorator extends Display { // 다양한 추가 기능에 대한 공통 클래스
	private Display decoratedDisplay;
    
    public DisplayDecorator(Display decoratedDisplay){
    	this.decoratedDisplay = decoratedDisplay;
    }
	public void draw(){
    	decoratedDisplay.draw();
    }
}

public class LaneDecorator extends DisplayDecorator { // 차선 표시를 추가하는 클래스
	public LaneDecorator(Display decoratedDisplay){
    	super(decoratedDisplay);
    }
	public void draw(){
    	super.draw();
        drawLane();
    }
	public void drawLane(){
    	System.out.println("차선 표시");
    }
}

public class TrafficDecorator extends DisplayDecorator { //교통량 표시를 추가하는 클래스
	public TrafficDecorator(Display decoratedDisplay){
    	super(decoratedDisplay);
    }
	
    public void draw(){
    	super.draw();
        drawTraffic();
    }
	
    private void drawTraffic(){
    	System.out.println("교통량 표시");
    }
}
public class Client {
	public static void main(String args[]){
    	Display road = new RoadDisplay(); // 기본 도로 표시
        road.draw();
        
        Display roadWithLane = new LaneDecorator(new RoadDisplay());
        roadWithLane.draw(); // 기본 도로표시 + 차선 표시 
        
        Display roadWithLaneAndTraffic = new TrafficDecorator(new LaneDecorator(new RoadDisplay()));
        roadWithLaneAndTraffic.draw(); // 추가 기능들의 조합
    }
}

 

→ Client 클래스는 동일한 Display 클래스만을 통해 일관성 있는 방식으로 도로 정보를 표시할 수 있다