티스토리 뷰

디자인 패턴

Decorator Pattern

기내식은수박바 2021. 10. 11. 14:38
반응형

데코레이터 패턴?

기본 기능에 추가할 수 있는 기능의 종류가 많은 경우, 필요한 기능들만을 골라 조합하여 설계하는 디자인 패턴

  • ex) 기본 도로 표시 기능 (+ 차선 표시, 교통량 표시, 교차로 표시, 단속 카메라 표시)
  • 4가지 추가 기능이 있을 때, 이 들의 조합은 총 15가지가 된다.
    • 데코레이터 패턴을 사용하면 이를 4가지 Decorator 클래스만 구현하여 객체 형태로 조합하여 추가 기능의 조합을 구현할 수 있다.

 

 

등장 배경

네비게이션에 아래와 같은 기능이 있다고 해보자. 그리고 이를 클래스 다이어그램과 코드로 표현하면 다음과 같다.

  • 기본 기능 : 도로를 간단한 선으로 표시
  • 추가 기능 : 도로의 차선을 표시하는 기능

// 기본 도로 표시 클래스
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("차선 표시");
    }
}

public class Client {
    public static void Main(String[] args) {
        RoadDisplay road = new RoadDisplay();
        road.draw(); // 기본 도로만 표시
        
        RoadDisplayWithLane roadWithLane = new RoadDisplayWithLane();
        roadWithLane.draw(); // 기본 도로 + 차선 표시
    }
}

 

 

문제점

(1) 또 다른 도로 표시 기능을 추가할 경우

교통량을 표시하는 클래스를 만들기 위해 RoadDisplayWithLane 클래스와 마찬가지로 RoadDisplay 클래스를 상속받아 RoadDisplayWithTraffic 클래스를 만든다.

// 기본 도로 표시 + 교통량 표시 클래스
public class RoadDisplayWithTraffic extends RoadDisplay {
    public void draw() {
        super.draw();
        drawTraffic();
    }
    
    private void drawTraffic() {
        System.out.println("교통량 표시");
    }
}

 

(2) 추가 기능을 조합해야 하는 경우

기본 기능 클래스의 하위 클래스로 추가 기능을 만드는 것은 적절할 수도 있지만, 기능의 다양한 조합을 고려할 경우에 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야 한다는 단점이 있다.

기본 기능에 추가로 사용할 수 있는 기능이 3가지 (차선 표시, 교통량 표시, 교차로 표시) 가 있다고 했을 때, 아래 표와 같이 8가지 조합이 가능하다.

 

이를 상속을 통해 설계한다면 각 조합별로 하위 클래스를 구현해야 한다. 아래는 추가 기능의 조합을 설계한 모습이다.

 

 

해결책

상속을 이용한 기능 추가 방법을 설명했지만, 이 방법은 추가 기능이 늘어날때마다 기능의 조합별로 하위 클래스를 구현해야 한다.

조합 수가 늘어나는 문제를 해결하기 위해서는 각 추가 기능별로 개별 클래스를 설계하고, 기능을 조합할 때 각 클래스의 객체 조합을 사용한다.

public abstract 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() {
        this.decoratedDisplay.draw();
    }
}

// 차선 표시 클래스
public class LaneDecorator extends DisplayDecorator {
    public LaneDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawLane();
    }
    
    private void drawLane() {
        System.out.println("\t차선 표시");
    }
}

// 교통량 표시 클래스
public class TrafficDecorator extends DisplayDecorator {
    public TrafficDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawTraffic();
    }
    
    private void drawTraffic() {
        System.out.println("\t교통량 표시");
    }
}
  • 도로를 표시하는 기본 기능만 필요하다면 RoadDisplay 객체만을 이용하면 된다.
    • 차선 표시 기능이 필요하다면 RoadDisplay + LandDecorator
    • 교통량 표시 기능이 필요하다면 RoadDisplay + TrafficDecorator

 

public class Client {
    public static void main(String[] args) {
        Display road = new RoadDisplay();
        road.draw(); // 기본 도로 표시
        
        Display roadWithLane = new RoadDisplay(new LaneDecorator());
        roadWithLane.draw(); // 기본 도로 표시 + 차선 표시
        
        Display roadWithTraffic = new RoadDisplay(new TrafficDecorator());
        roadWithTraffic.draw(); // 기본 도로 표시 + 교통량 표시
    }
}
  • road, roadWithLane, roadWithTraffic 객체의 접근 모두 Display 클래스를 통해 이루어진다.
    • 즉, 어떠한 기능을 추가하던 상관 없이 Client 클래스는 Display 클래스만을 이용하여 일관성 있는 방식으로 도로 정보를 표시할 수 있다.

 

교차로를 표시하는 기능을 추가해보자.

// 교차로 표시 클래스
public class CrossingDecorator extends DisplayDecorator {
    public CrossingDecorator(Display displayDecorator) {
        super(displayDecorator);
    }
    
    public void draw() {
        super.draw();
        drawCrossing();
    }
    
    private void drawCrossing() {
        System.out.println("\t교차로 표시");
    }
}

public class Client {
    public static void main(String[] args) {
        Display roadWithLaneTrafficCrossing = new RoadDisplay(
            new LaneDecorator(new TrafficDecorator(new CrossingDecorator()))
        );
        roadWithLaneTrafficCrossing.draw(); // 기본 도로 표시 + 차선 표시 + 교통량 표시 + 교차로 표시
    }
}

 

 

Reference

  • 도서 'UML과 GoF 디자인 패턴 핵심 10가지로 배우는 JAVA 객체 지향 디자인 패턴'
반응형

'디자인 패턴' 카테고리의 다른 글

Factory Method Pattern  (0) 2021.12.04
Singleton Pattern  (0) 2021.10.02
Template Method Pattern  (0) 2021.09.26
Strategy Pattern  (0) 2021.09.15
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함