[구조 패턴] Chapter 9. Decorator Pattern

 

데코레이터 패턴, 소개

데코레이터 패턴은 기존의 객체를 수정하지 않고 새로운 기능을 추가하거나 확장할 수 있게 만드는 패턴으로, 기존 클래스의 구조를 변경하지 않고 런 타임에 부가 기능을 추가하는 장점을 가지고 있다.

 

데코레이터 패턴은 다음과 같은 상황에 유용하다.

첫째로, 기존의 클래스를 변경하기 어렵고 동시에 새로운 기능이나 행동을 추가해야 할 때

둘째로, 여러 기능을 일련의 순서에 맞게 조합해서 사용해야 하는 경우 유용하다.

 

정리하면, 데코레이터 패턴의 핵심은 기존의 객체를 변경하지 않고도 새로운 기능을 추 할 수 있다는 점이다. 이는 객체 지향의 개방/폐쇄 원칙을 따르며, 코드의 유지 보수와 확장성을 높일 수 있다.

 

 

데코레이터 패턴, 코드

before

CommentProcessor.java

단순 String을 출력하는 기본 클래스

public class CommentProcessor {

    public void addComment(String comment) {
        System.out.println(comment);
    }
    
}

 

TrimmingCommentProcessor.java

특정 문자를 제거하는 기능이 추가로 필요하다면 상속을 이용해서 해결할 수 있다.

public class TrimmingCommentDecorator extends CommentProcessor {

    @Override
    public void addComment(String comment) {
        super.addComment(trim(comment));
    }

    private String trim(String comment) {
        return comment.replace("...", "");
    }
}

 

SpamFilteringCommentDecorator.java

spam을 제거하는 기능이 필요하다면 마찬가지로 상속을 이용할 수 있다.

public class SpamFilteringCommentDecorator extends CommentProcessor {

    @Override
    public void addComment(String comment) {
        if (!isSpam(comment))
            super.addComment(comment);
    }

    private boolean isSpam(String comment) {
        return comment.contains("[spam]");
    }

}

 

상속 구조의 문제는 TrimmingSpamFiltering을 동시에 제공하는 기능이 필요하다면 또다시 새로운 클래스를 만들어야 한다. 다르게 말하면 여러 기능을 제공하는 클래스가 있을 때 선택적으로 기능을 조합해서 사용하기 어렵다고 할 수 있다. 특히 런타임에 더욱 그렇다.

 

 

after

CommentProcessor.java

모든 기능은 CommentProcessor 인터페이스를 구현함으로 써 제공한다.

public interface CommentProcessor {
    void process(String comment);
}

 

PrintCommentProcessor.java

기본적인 기능을 제공할 구현체. 표에서는 ConcreteComponent에 해당한다.

public class PrintCommentProcessor implements CommentProcessor {

    @Override
    public void process(String comment) {
        System.out.println(comment);
    }

}

 

CommentDecorator.java

데코레이터 개념을 추상화한 클래스

@AllArgsConstructor
public abstract class CommentDecorator implements CommentProcessor {

    private CommentProcessor delegate;

    @Override
    public void process(String comment) {
        delegate.process(comment);
    }
}

 

SpamFilteringCommentDecorator.java, TrimmingCommentDecorator.java

데코레이터 기능을 구현한 클래스로 CommentDecorator를 상속받는다.

public class SpamFilteringCommentDecorator extends CommentDecorator {

    public SpamFilteringCommentDecorator(CommentProcessor commentProcessor) {
        super(commentProcessor);
    }

    @Override
    public void process(String comment) {
        if (!isSpam(comment))
            super.process(comment);
    }

    private boolean isSpam(String comment) {
        return comment.contains("[spam]");
    }

}
public class TrimmingCommentDecorator extends CommentDecorator {

    public TrimmingCommentDecorator(CommentProcessor commentProcessor) {
        super(commentProcessor);
    }

    @Override
    public void process(String comment) {
        super.process(trim(comment));
    }

    private String trim(String comment) {
        return comment.replace("...", "");
    }
}

 

예제 코드

기본적으로 제공할 기능에 동적으로 데코레이터들을 붙여가며 기능을 확장할 수 있다.

public class App {

    private static boolean ENABLE_SPAM_FILTER = true;
    private static boolean ENABLE_TRIM = true;

    public static void main(String[] args) {
        CommentProcessor commentProcessor = new PrintCommentProcessor();
        if (ENABLE_SPAM_FILTER)
            commentProcessor = new SpamFilteringCommentDecorator(commentProcessor);
        if (ENABLE_TRIM)
            commentProcessor = new TrimmingCommentDecorator(commentProcessor);

        Client client = new Client(commentProcessor);
        client.writeComment("hello world!...");     // ==> hello world!
        client.writeComment("[spam] hello world!"); // ==> nothing
    }

}

 

 

인프런의 백기선님의 강의 코딩으로 학습하는 GoF의 디자인 패턴을 참고해서 작성했습니다.

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com