디자인패턴

데코레이터(Decorator) 패턴

JUNGKEUNG 2022. 8. 4. 00:34

데코레이터 패턴 (Decorator Pattern) 이란?

기존에 있는 코드를 변경하지 않고 부가적인 기능을 추가하는 패턴이다.

 

 

 

데코레이터 사용 전

 

Comment 를 관리하는 CommentService 는 아래와 같으며 addComment 는 Comment 를 출력하는 메서드이다.

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

 

SpamFiltering 은 http 프로토콜을 받기 싫다면 그에 맞는 기능을 추가해야한다.

해당 기능은 아래와 같이 SpamFilteringCommentService로 구현할 수 있다

public class SpamFilteringCommentService extends CommentService {

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

    private boolean isSpam(String comment) {
        return comment.contains("http");
    }
}

 

만약, "..." 과 특수기호는 관리하고 싶지 않다면 그에 맞는 기능을 추가해야한다.

해당 기능은 아래와같이 TrimmingCommentService 로 구현할 수 있다.

 

public class TrimmingCommentService extends CommentService {

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

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

}

 

이를 사용하기 위해 Client는 아래와 같이 의존관계만 변경해주면된다.

 

public class Client {

    private CommentService commentService;

    public Client(CommentService commentService) {
        this.commentService = commentService;
    }

    private void writeComment(String comment) {
        commentService.addComment(comment);
    }

    public static void main(String[] args) {
        Client client = new Client(new SpamFilteringCommentService());
        client.writeComment("오징어게임");
        client.writeComment("보는게 하는거 보다 재밌을 수가 없지...");
        client.writeComment("http://whiteship.me");
    }

}

 

문제발생

  • SpamService , TrimService 모두 사용하려면 이 두 코드가 포함된 새로운 CommentService 를 만들어야 한다.
  • 아니면, 새로운 CommentService 를 만들어 이 둘을 이중 상속받아야 한다. 

이를 데코레이터 패턴을 적용하여 해결해보자.

 

 

 

데코레이터 패턴 적용 후

먼저 공통된 기본 기능을 가진 CommentService 인터페이스

그리고 그 기본 기능을 구현하는 DefaultCommentService 는 아래와 같다.

 

Interface CommentService 생성

public interface CommentService {

    void addComment(String comment);
}

 

 

DefaultCommentService

public class DefaultCommentService implements CommentService{

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

CommentDecorator

public class CommentDecorator implements CommentService{

    private CommentService commentService;

    public CommentDecorator(CommentService commentService){
        this.commentService = commentService;
    }

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

SpamFilteringCommentDecorator

public class SpamFilteringCommentDecorator extends CommentDecorator{

    public SpamFilteringCommentDecorator(CommentService commentService) {
        super ( commentService );
    }

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

    private boolean isNotSpam(String comment) {
        return comment.contains("http");
    }
}

TrimmingCommentDecorator

public class TrimmingCommentDecorator extends CommentDecorator{

    public TrimmingCommentDecorator(CommentService commentService) {
        super (commentService);
    }

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

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

client

public class Client {

    private CommentService commentService;

    public Client(CommentService commentService){
        this.commentService = commentService;
    }

    public void writeComment(String comment) {
        commentService.addComment(comment);
    }
}

App

public class App {

    private static boolean enabledSpamFilter = true;
    private static boolean enabledTrimming = true;

    public static void main(String[] args) {
        CommentService commentService = new DefaultCommentService ();

        if(enabledSpamFilter) {
            commentService = new SpamFilteringCommentDecorator(commentService);
        }

        if(enabledTrimming) {
            commentService = new TrimmingCommentDecorator(commentService);
        }

        Client client = new Client(commentService);
        client.writeComment("오징어게임");
        client.writeComment("보는게 하는거 보다 재밌을 수가 없지");
        client.writeComment("http://whiteship.me");
    }
}

 위와 같이 데코레이터를 사용하면 단일 책임 원칙, 개방 폐쇄 원칙을 지키면서 새로운 클래스를 만들지 않고도 기존 기능을 조합할 수 있다. 하지만 기능이 늘어날수록 코드가 복잡해질수가 있다.

 

 

장점

  • 새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다
  • 컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다

단점

  • 데코레이터를 조합하는 코드가 복잡할 수 있다

 

 

참고


 

 

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

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

www.inflearn.com

 

 

데코레이터 패턴(Decorator Pattern)

데코레이터 패턴(Decorator Pattern) 기존 코드를 변경하지 않고 부가기능을 추가하는 패턴 상속이 아닌 위임을 사용해서 보다 유연하게 (런타임에) 부가 기능을 추가하는 것도 가능하다. 데코레이

hyokeun0419.tistory.com

 

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

플라이웨이턴 패턴 (Flyweight Pattern)  (0) 2022.08.15
퍼사드 패턴(Facade Pattern)  (0) 2022.08.14
컴포짓(composite) 패턴  (0) 2022.07.30
브릿지(Bridge)패턴  (0) 2022.07.20
프로토타입(Prototype) 패턴  (0) 2022.07.08