개발 이야기/디자인 패턴

[디자인 패턴] 데코레이터(Decorater) 패턴이란?

제이온 (Jayon) 2021. 3. 15.

안녕하세요? 제이온입니다.

 

저번 시간에는 상태 패턴에 대해서 알아 보았습니다. 오늘은 데코레이터 패턴을 설명하겠습니다.

 

 

데코레이터(Decorater) 패턴

데코레이터 패턴은 객체에 추가적인 요건을 동적으로 추가한 것을 말합니다. 여기서 동적으로 추가할 때는 보통 특정 객체를 결합하는 방식을 사용합니다.

 

 

 

 

위는 전반적인 데코레이터 패턴의 구조입니다. 여기서 Component는 기본 기능을 담당하는 ConcreteComponent와 추가 기능을 담당하는 Decorator의 공통 기능을 정의합니다.

 

ConcreateDecorator은 Decorator의 하위 클래스로, Component의 기본 기능과 Decorator의 추가 기능을 모두 제공합니다. 그래서 ConcreateDecorator는 ConcreteComponent에 대한 참조가 필요한데, 조합을 통해서 표현합니다. 이 부분은 예제를 통해 보시는 것이 이해하기 쉽습니다.

 

 

데코레이터(Decorater) 패턴 예제

특정 라면에는 어떠한 특징이 있는지 출력하는 프로그램을 만든다고 합시다. 예를 들어, 신라면에는 '면'과 '매운 조미료'가 들어간다고 할 수 있고, 너구리에는 '면'과 '다시마'가 들어간다고 할 수 있습니다. 그렇다면, 아래와 같이 구조를 설계할 수 있습니다.

 

 

 

 

<Noodle>

public interface Noodle {
    void assemble();
}

 

 

<Ramen>

public class Ramen implements Noodle {

    @Override
    public void assemble() {
        System.out.print("면이 들어간다. ");
    }
}

 

 

<ShinRamen>

public class ShinRamen extends Ramen {
    @Override
    public void assemble() {
        super.assemble();
        System.out.print("매운 조미료가 들어간다. ");
    }
}

 

 

<RaccoonRamen>

public class RaccoonRamen extends Ramen {
    @Override
    public void assemble() {
        super.assemble();
        System.out.print("다시마가 들어간다.");
    }
}

 

 

위와 같이 클래스를 정의할 수 있습니다. 그런데, 만약 여기서 '신너구리라면'이 나온다면 어떨까요?

 

 

 

 

맘같아서는 신너구리라면이 신라면과 너구리에 다중상속을 하고 싶지만, 자바에서는 클래스 간의 다중상속을 지원하지 않습니다. 물론, 다중상속을 지원하더라도 계속해서 신너구리참깨라면 같은 라면이 출시된다면 클래스의 수가 기하급수적으로 증가하게 됩니다.

 

따라서, 기본 기능인 '면'은 고정으로 하되 나머지 추가 기능을 개별적인 클래스로 정의하여 이를 결합하도록 설계해야합니다.

 

 

 

 

위가 바로 데코레이터 패턴을 적용한 구조입니다. 관건은 RamenDecorator인데, 코드를 먼저 보겠습니다.

 

 

public class RamenDecorator implements Noodle {
    private Noodle noodle;

    public RamenDecorator(final Noodle noodle) {
        this.noodle = noodle;
    }

    @Override
    public void assemble() {
        noodle.assemble();
    }
}

 

 

Noodle을 상속받아서 assemble() 메소드를 오버라이드해 주는 것은 당연합니다. 여기서 주목할 점은 기본 기능인 Ramen를 참조하기 위하여 조합을 사용하였다는 점입니다. 필드에 Noodle 변수를 정의하고, 생성자를 통해 값을 넘겨받으면 됩니다.

 

 

<ShinRamenDecorator>

public class ShinRamenDecorator extends RamenDecorator {

    public ShinRamenDecorator(final Noodle noodle) {
        super(noodle);
    }

    @Override
    public void assemble() {
        super.assemble();
        System.out.print("매운 조미료가 들어간다. ");
    }

}

 

 

<RaccoonRamenDecorator>

public class RaccoonRamenDecorator extends RamenDecorator {

    public RaccoonRamenDecorator(final Noodle noodle) {
        super(noodle);
    }

    @Override
    public void assemble() {
        super.assemble();
        System.out.print("다시마가 들어간다. ");
    }
}

 

 

noodle 변수에는 Ramen이 들어올 것이므로 각각 super.assemble() 메소드를 실행하면 '면이 들어간다.'라는 출력문이 뜰 것입니다. 이제, 우리는 추가 기능을 각자 만들어주면 됩니다.

 

 

필요한 코드가 모두 작성되었습니다. 이제, 클라이언트에서 데코레이터 패턴을 어떻게 이용하는지 살펴봅시다.

 

 

public class Application {

    public static void main(String[] args) {
        final Noodle ramen = new Ramen();
        ramen.assemble();
        System.out.println();

        final Noodle shinRamen = new ShinRamenDecorator(new Ramen());
        shinRamen.assemble();
        System.out.println();

        final Noodle raccoonRamen = new RaccoonRamenDecorator(new Ramen());
        raccoonRamen.assemble();
        System.out.println();

        final Noodle shinRaccoonRamen = new RaccoonRamenDecorator(new ShinRamenDecorator(new Ramen()));
        shinRaccoonRamen.assemble();
        System.out.println();
    }
}

 

 

데코레이터 패턴의 최대 장점은 이미 있는 여러 가지 특징의 라면을 하나의 클래스로 분리하는 것이 아니라, 데코레이터를 이어붙여서 정의할 수 있다는 것입니다. 특히, 신너구리라면은 너구리 데코레이터와 신라면 데코레이터를 사용하여 구현되었음을 알 수 있습니다.

 

한 번 실행해 봅시다.

 

 

 

 

우리가 생각한 특징들이 잘 나오는 것을 알 수 있습니다.

 

 

정리

지금까지 데코레이터 패턴을 알아 보았습니다. 데코레이터 패턴은 런타임에서 유연하게 객체의 기능들을 수정하고 조합하는데 유용하게 사용되는 패턴이라고 할 수 있습니다. 다만, 조합해야할 특징이 많다면 이어붙이는 데코레이터의 수까지 늘어나므로 가독성이 해치는 단점이 있습니다.

 

 

출처

개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 최범균

 

 

[Design Pattern] 데코레이터 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

 

 

[구조 패턴] 데코레이터 패턴(Decorator Pattern) 이해 및 예제

데코레이터 패턴(Decorator Pattern)은 Flyweight 패턴, Adapter 패턴, Bridge 패턴처럼 구조 패턴 중 하나로, 런타임에서 객체의 기능을 수정하는데 사용되는 패턴입니다. 구조 패턴(Structural Pattern)이란? 구..

readystory.tistory.com

 

댓글

추천 글