티스토리 뷰

디자인 패턴

09. 데코레이터 패턴

jin-park 2022. 2. 10. 17:40

1. 의미

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

다이어그램

2. 작성 방법

2.1. 기존 코드에 부가기능 추가 시, 상속의 한계

  • CommentService : 댓글 서비스
  • TrimmingCommentService :  CommentService상속, Trim 부가기능 추가
  • SpamFilteringCommentService : CommentService상속, SpamFilter 부가기능 추가
  • Trim기능과, SpamFilter 기능을 모두 사용하고 싶다면 새로운 서비스를 추가해야하는 상황이 발생 -> 유연하지 않는 코드
public class CommentService {
    public void addComment(String comment) {
        System.out.println(comment);
    }
}
public class TrimmingCommentService extends CommentService {
    @Override
    public void addComment(String comment) {
        super.addComment(trim(comment));
    }
 
    private String trim(String comment) {
        return comment.replace("...", "");
    }
}
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");
    }
}
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());  //기능추가에 따른 생성자 호출 변경 (TrimmingCommentService,SpamFilteringCommentService)
        client.writeComment("오징어게임");
        client.writeComment("보는게 하는거 보다 재밌을 수가 없지...");
        client.writeComment("http://whiteship.me");
    }
 
}

2.2. 데코레이턴 패턴으로 부가기능을 추가

  • CommentService : 인터페이스 정의
  • DefaultCommentService : ConcreateComponent정의 , CommentService에서 하던일
  • CommentDecorator : ConcreateComponent를 호출하는 메인 Decorator
  • TrimmingCommentDecorator : 메인 Decorator를 상속받은 부가 Decorator
  • SpamFilteringCommentDecorator : 메인 Decorator를 상속받은 부가 Decorator

public interface CommentService {
    void addComment(String comment);
}
public class DefaultCommentService implements CommentService {
    @Override
    public void addComment(String comment) {
        System.out.println(comment);
    }
}
public class CommentDecorator implements CommentService {
    private CommentService commentService;
 
    public CommentDecorator(CommentService commentService) {
        this.commentService = commentService;
    }
 
    @Override
    public void addComment(String comment) {
        commentService.addComment(comment);
    }
}
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("...", "");
    }
}
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");
    }
}
public class Client {
 
    private CommentService commentService;
        
    private static boolean enabledSpamFilter = true;
 
    private static boolean enabledTrimming = true;
 
    public Client(CommentService commentService) {
        this.commentService = commentService;
    }
 
    public void writeComment(String comment) {
        commentService.addComment(comment);
    }

    public static void main(String[] args) {
        //9Line ~ 17Line 코드가 늘어나는건 어쩔 수 없음
        //객체를 조합해서 동적으로 전달해주는것으로 생각해주자!
        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");
    }
}

3. 장점과 단점

  • 장점
    • 새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다.
      • 단일 책임의 원칙 : 각자 해야할 일만 하면 된다.
    • 컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다.
      • 개방 폐쇄의 원칙 : 확장에는 개방되어있고, 수정에 있어서는 닫혀있는 구조가 되어야한다는 원칙
      • 의존 관계 역전의 원칙 : 상위 모듈은 하위 모듈의 구현에 의존하면 안되며, 하위 모듈이 상위 모듈에서 정의한 추상 타입에 의존해야하는 원칙
    • 대부분의 객체지향 원칙이 적용되어있어 굉장히 좋은 패턴
  • 단점
    • 데코레이터를 조합하는 코드가 복잡할 수 있다.

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

11. 플라이 웨이트 패턴  (0) 2022.02.10
10. 퍼사드 패턴  (0) 2022.02.10
08. 컴포짓 패턴  (0) 2022.02.10
07. 브릿지 패턴  (0) 2022.02.10
06. 어댑터 패턴  (0) 2022.02.10
댓글