Decorator

GamSa Ham·2022년 10월 15일
0

GoF디자인패턴

목록 보기
5/22

의도

  • 객체에 동적으로 새로운 책임을 추가할 수 있게 합니다. 기능을 추가하려면, 서브 클래스를 생성하는 것보다 융통성 있는 방법을 제공합니다.

다른이름

  • 랩퍼(Wrapper)

동기

  • 윈도우에 Text를 출력하는 서비스를 제공하는 TextView 클래스가 있다고 가정, TextView에 스크롤바 기능이 추가 되어야 한다고 하면 ScrollDecorator 를 사용해서 TextView를 추가할수 있습니다. 또 두꺼운 테두리가 필요한 Border기능을 넣을려면 BorderDecorator을 사용해서 추가합니다.

활용성

  • 동적으로 또한 투명하게, 다시 말해 다른 객체에 영향을 주지 않고 개개의 객체에 새로운 책임을 추가하기 위해
  • 제거 될수 있는 책임에 대해 사용합니다.
  • 실제 상속으로 서브클래스를 계속 만드는 방법이 실질 적이지 못할때 사용합니다. 상속으로 해결하면 클래스 수가 폭팔적으로 많아지게 됩니다.

구조

참여자

  • Component(VisualComponent): 동적으로 추가할 서비스를 가질 가능성이 있는 개체들에 대한 인터페이스
  • ConcreateComponent(TextView): 추가적인 서비스가 실제로 정의되어야 할 필요가 있는 객체
  • Decorator: Component 객체에 대한 참조자를 관리하면서 Component에 정의된 인터페이스를 만족하도록 인터페이스를 정의
  • ConcreteDecorator(BorderDecorator, ScrollDecorator): Component에 새롭게 추가할 서비스를 실제로 구현하는 클래스

결과

  • 장식자 패턴을 쓰면서 얻는 이익과 부담은 각각 두가지 입니다.

      1. 단순한 상속보다 설계의 융통성을 더 많이 증대시킬 수 있다.

(만약 상속으로 구현을 해야합니다. ScrollableTextView, BorderedTextView 의 인터페이스 를 만들어야 하고 스크롤과 Border기능이 같이 있는 TextView 를 만들려면 BorderedScrollableTextView 클래스를 생성해야합니다. 이는 새로운 기능이 추가될때 마다 클래스가 추가되게 된다.)

    1. 클래스 계통의 상부측 클래스에 많은 기능이 누적되는 상황을 피할수 있습니다.

    ( 필요한 비용만 그때 지불하는 방법)

    1. 장식자와 해당 그 장식자의 구성요소가 동일한 것은 아닙니다.
    1. 장식자를 사용함으로써 작은 규모의 객체들이 많이 생깁니다.

구현

package study.designpattern;

class VisualComponent {
    public VisualComponent() {}
    public void draw() {

        System.out.println("VisualComponent.draw");
    }
    public void resize() {}

    // ...
}

class Decorator extends VisualComponent {
    public Decorator(VisualComponent visualComponent) {
        this.visualComponent = visualComponent;
    }

    @Override
    public void draw() {
        visualComponent.draw();

    }

    @Override
    public void resize() {
        visualComponent.resize();
    }

    private VisualComponent visualComponent;
}

class BorderDecorator extends Decorator {
    public BorderDecorator(VisualComponent visualComponent) {
        super(visualComponent);
    }

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

        System.out.println("BorderDecorator.draw");
    }

    private void drawBorder(int val) {}
    private int width;

}

class ScrollDecorator extends Decorator {
    public ScrollDecorator(VisualComponent visualComponent) {
        super(visualComponent);
    }

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

        System.out.println("ScrollDecorator.draw");
    }

    private void scroll(int val) {}
    private int top;
}

class Window {
    public void setContent(VisualComponent visualComponent) {
        this.visualComponent = visualComponent;
    }

    private VisualComponent visualComponent;

    public VisualComponent getVisualComponent() {
        return visualComponent;
    }
}

class TextView extends VisualComponent{
}

public class DecoratorPattern {
    public static void main(String[] args) {
        Window window = new Window();
        TextView textView = new TextView();

//        // 일반 textView 를 사용할 경우
//        window.setContent(textView);
//
//        // border를 사용할 경우
//        window.setContent(new BorderDecorator(textView));

        // border와 스크롤을 사용할 경우
        window.setContent(new BorderDecorator(new ScrollDecorator(textView)));

        window.getVisualComponent().draw();
    }
}

코드에서 보면 객체를 리턴받아 체인 형식으로 연결하는 것이 아닌 다형성을 활용해 Decorator 항목을 행동을 추가하는것을 볼수 있다.

Window는 VisualComponent 인터페이스를 통해 자신의 정보를 접근하기 때문에 장식자의 존재 자체를 알 수 없습니다.

다이어그램

관련 패턴

  • 장식자 패턴은 적응자 패턴과 관련이 있습니다. 즉 원래의 적응자는 인터페이스를 변경시켜 주는 것이지만, 장식자는 객체의 책임, 행동을 변화시킵니다.

  • 복합체 패턴과도 관련이 됩니다. 한 구성요소만 갖는 복합체라고 볼수 있습니다, 객체의 합성이 아니라 새로운 행동을 추가

  • 전략 패턴과도 관련이 됩니다. 장식자는 객체의 겉모양을 변경하고 전략은 객체의 내부를 변화시킵니다.

     ( 전략패턴 항목은 내부에 필드에 추가되는 항목으로 알고있음)

디자인 항목

  • SOILD 항목에 OCP(Open-Closed-Principle, 계방폐쇠원칙) 항목에 부합한다.

    소프트웨어 구성 요소(컴포넌트, 클래스, 모듈, 함수)나 확장에는 열려있고 변경에는 닫혀 있어야 한다.

profile
안녕하세요. 자바를 좋아하고 디자인 패턴, Refactoring, Clean Code에 관심이 많은 백엔드 개발자입니다.

0개의 댓글