반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 메이븐 골
  • 메이븐 페이즈
  • 도커공식문서
  • Pre-rendering
  • 자바
  • item7
  • 자료구조
  • 싱글톤
  • 싱글톤 패턴
  • 외래키 제약조건
  • 자바스크립트 면접
  • 플라이웨이트패턴
  • NEXT JS
  • 디자인패턴
  • 토비의 스프링
  • prerendering
  • 객체복사
  • 자바스크립트
  • item8
  • Java
  • 이펙티브 자바 item9
  • next js app
  • 작업기억공간
  • 자바 디자인패턴
  • 프로그래머의 뇌
  • Next.js
  • 러스트
  • MSSQL
  • 스프링 검증
  • rust
  • pnpm
  • 이펙티브자바
  • 슬로우 쿼리
  • 자바 검증
  • 참조 해제
  • bean Validation
  • 이펙티브 자바
  • 자바스크립트 인터뷰
  • 싱글턴
  • serverless computing
  • item9
  • try-with-resources
  • 빈 검증
  • 팩터리 메서드 패턴
  • 메이븐 라이프사이클
  • 서버리스 컴퓨팅
  • 느린 쿼리
  • 추상 팩터리 패턴
  • Javadoc 자바독 자바주석 주석 Comment
  • 알고리즘

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

데코레이터 패턴 (Decorator Pattern) 이란?
Java/자바 디자인 패턴

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

2023. 2. 25. 13:11

데코레이터 패턴 (Decorator pattern)

  • 래퍼 객체를 이용해 모듈과 비슷한 방식으로 기존 객체에 기능을 추가할 수 있다.
    • 기존 기능에 영향을 주지 않고 가능하다.
  • 런타임에 객체에 '행위' 혹은 '기능'을 추가할 수 있게 해준다.
  • 기존 객체를 '행위'를 가진 특별한 래퍼 객체 (데코레이터)에 넣어서 객체가 그 '행위'를 할 수 있게 만든다.
  • 캐싱, 로깅, 검증과 같은 기능에 쓰일 수 있다.

피자 클래스 데코레이터 패턴 적용 예제

  • 피자를 클래스로 매핑하여 만들어보려고 한다.

데코레이터 패턴 적용 전

  • 데코레이터 패턴 적용 전의 코드를 보자

Pizza 생성

public class Pizza {
    protected String pizzaName() {
        return "피자";
    }
}
  • 일반 피자를 만들었다.

CheesePizza, BulgogiPizza 추가

  • 치즈 피자와 불고기 피자를 만들어야 하는 요구사항이 들어왔다.
  • 상속을 이용해 간단히 구현했다.
public class CheesePizza extends Pizza {
    @Override
    protected String pizzaName() {
        return "치즈 " + super.pizzaName();
    }
}
public class BulgogiPizza extends Pizza {
    @Override
    protected String pizzaName() {
        return "불고기 " + super.pizzaName();
    }
}
  • 이렇게 치즈 피자 와 불고기 피자 도 만들었는데,
  • 손님들의 반응이 좋아서 치즈 불고기 피자 를 만들고 싶다.
  • 기존의 상속 방식을 그대로 이용하려면, 치즈 피자를 상속해서 만들거나 불고기 피자 를 상속해서 치즈 불고기 피자 클래스를 만들 수는 있다.
  • 갑자기, '나중에 씬도우, 치즈 크러스트 등의 피자가 추가되면, 치즈크러스트 치즈 불고기 피자, 씬도우 치즈크러스트 치즈 불고기 피자, 씬도우 불고기 피자, 씬도우 치즈 피자 등 수 없이 많은 새로운 클래스를 만들어야될까?' 라는 생각이 들었다.
  • 이 문제를 해결하기 위해 데코레이터 패턴을 적용하기로 했다.

데코레이터 패턴 적용 후

  • 아래는 데코레이터 패턴을 적용한 코드이다.

PizzaService 인터페이스 생성

public interface PizzaService {
    String pizzaName();
}
  • 모든 피자에서 수행할 서비스를 정의하는 인터페이스이다.

DefaultPizza 클래스 생성

public class DefaultPizza implements PizzaService{
    @Override
    public String pizzaName() {
        return "피자";
    }
}
  • 기본 피자이다.

PizzaDecorator 추상 클래스 생성

public abstract class PizzaDecorator implements PizzaService {
    PizzaService pizzaService;

    public PizzaDecorator(PizzaService pizzaService) {
        this.pizzaService = pizzaService;
    }

    @Override
    public String pizzaName() {
        return pizzaService.pizzaName();
    }
}
  • 피자를 꾸며주는 PizzaDecorator 추상 클래스이다.
  • 추상 클래스인만큼 자체적으로 인스턴스가 될 수는 없다.

CheeseDecorator 클래스 생성

public class CheeseDecorator extends PizzaDecorator{
    public CheeseDecorator(PizzaService pizzaService) {
        super(pizzaService);
    }

    @Override
    public String pizzaName() {
        return "치즈 " + super.pizzaName();
    }
}
  • 치즈를 추가할 때 사용한다.

BulgogiDecorator 클래스 생성

public class BulgogiDecorator extends PizzaDecorator{
    public BulgogiDecorator(PizzaService pizzaService) {
        super(pizzaService);
    }

    @Override
    public String pizzaName() {
        return "불고기 " + super.pizzaName();
    }
}
  • 불고기를 추가할 때 사용한다.

Client 코드 생성

public class Client {
    private static boolean enabledBulgogi = true;
    private static boolean enabledCheese = true;

    public static void main(String[] args) {
        PizzaService pizza = new DefaultPizza();

        if (enabledBulgogi) {
            pizza = new BulgogiDecorator(pizza);
        }

        if(enabledCheese) {
            pizza = new CheeseDecorator(pizza);
        }

        System.out.println(pizza.pizzaName());
    }
}
  • 불고기가 활성화 되어있으면, PizzaService 타입에 데코레이터 클래스를 랩핑해서 새로운 PizzaService 를 반환받는 식으로 데코레이터 패턴을 구현했다.
  • 이제 런타임에 마음껏 피자 토핑을 추가할 수 있다.
  • 데코레이터가 데코레이터를 감싸는 게 어찌보면 유연한 느낌의 상속처럼 바라볼 수도 있다.

다이어그램으로 살펴보기

  • Component 가 이전의 PizzaService 에 해당한다.
  • Concrete Component 는 DefaultPizza 이다.
  • Decorator 는 PizzaDecorator
  • Concrete Decorator 가 각각 CheeseDecorator, BulgogiDecorator 이다.

현실 예제

  • 알람 서비스와 관련된 현실 예제를 살펴보자.

초기 상황

  • 이메일 알람 서비스를 제공하는 일을 하는 회사에서 근무 중이다.
  • Notifier 라는 기존 클래스가 존재한다.
    • 등록된 회원의 이메일 주소에 일괄적으로 이메일을 보내주는 역할을 한다.
  • 클라이언트는 초기 세팅 후 Notifier 클래스의 send() 메서드를 이메일을 마음껏 보낼 수 있었다.

Notifier 의 현재 소스코드

public class Notifier {
    private final ArrayList<String> emails = new ArrayList<>();

    public void addEmail(String email) {
        emails.add(email);
        System.out.println("이메일 \"" + email + "\" 가 성공적으로 수신 이메일 목록에 추가되었습니다.");
    }

    public void send(String message) {
        for (String email : emails) {
            sendEmail(email, message);
        }
    }

    private void sendEmail(String email, String message) {
        System.out.println("이메일 주소: \"" + email + "\" 로 내용: \"" + message + "\" 을 보냈습니다.");
    }
}

Client 의 현재 소스코드

public class Client {
    public static void main(String[] args) {
        Notifier notifier = new Notifier();

        notifier.addEmail("n00nietzsche@gmail.com");
        notifier.addEmail("billgates@microsoft.com");

        notifier.send("하이.");
    }
}

/*
출력 결과:
이메일 "n00nietzsche@gmail.com" 가 성공적으로 수신 이메일 목록에 추가되었습니다.
이메일 "billgates@microsoft.com" 가 성공적으로 수신 이메일 목록에 추가되었습니다.
이메일 주소: "n00nietzsche@gmail.com" 로 내용: "안녕하세요." 을 보냈습니다.
이메일 주소: "billgates@microsoft.com" 로 내용: "안녕하세요." 을 보냈습니다.
*/

추가 요구사항

  • 시대가 변하면서 알람 시스템에도 새로운 요구사항이 들어왔다.
  • 많은 알람 채널을 지원해야될 필요가 있었다.
    • 문자 메세지, 슬랙, 페이스북과 같은 메세지도 보낼 수 있도록 해달라는 요구사항이었다.
  • 새로운 메세징 시스템이 지속적으로 추가되고 있어 이에 대비할 필요도 생겼다.
    • ex) 디스코드, 트위터, 마스토돈 등...

상속을 사용할까?

  • 처음에는 상속을 통해 구현하는 것을 고려할 수 있다.
  • Notifier 를 상속하는 SmsNotifier, FacebookNotifier, SlackNotifier 와 같은 서브 클래스를 생성하여 구현할 수 있다.
  • 상속이 갖는 치명적인 단점들이 꽤 있기 때문에 되도록 1차원적 상속을 자제하고 최소한 합성을 사용하려 한다.
  • 그리고 프로그램을 실행 중인 런타임에 알람 채널을 바꿀 수 있으면 좋겠다는 생각이 들어서 1차원적 상속은 확실히 제외하기로 했다.
  • 그리고 사용자들에게는 적은 이질감을 느끼게 만들기 위해 최소한의 클라이언트 코드만 바꾸고 싶다.

데코레이터 패턴 구현하기

  • 데코레이터 패턴을 구현하여 위의 요구사항을 충족해보자

NotifierInterface 생성

public interface NotifierInterface {
    void send(String message);
}
  • 현재 Notifier 구현에서 Notifier 의 종류에 따라 유일하게 달라질 수 있는 부분은 send() 메서드이다.

NotifierBaseDecorator 추상 클래스 생성

public abstract class NotifierBaseDecorator implements NotifierInterface {
    public final Notifier notifier;

    public NotifierBaseDecorator(Notifier notifier) {
        this.notifier = notifier;
    }
}
  • 베이스 데코레이터의 역할을 할 추상 클래스이다.
  • 주입된 Notifier 가 가지는 모든 메서드나 필드를 '사용'할 수 있다.
    • 상속 관계와 비교되는 합성 관계를 사용한 것이다.
  • 생성자를 통해 기존의 Notifier 를 감싸면, Notifier 클래스를 멤버로 넣고 이에 변화를 줄 수 있는 방식으로 구현했다.
    • 이 추상 클래스를 상속하여 구현한 클래스가 말 그대로 행위를 가진 특별한 래퍼 객체 가 될 것이다.
  • Notifier 내부에 ArrayList 형태로 notifiers 라는 멤버를 만들어 거기에 데코레이터들을 보관할 것이다.

EmailNotifierDecorator 생성

public class EmailNotifierDecorator extends NotifierBaseDecorator {
    public EmailNotifierDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public void send(String message) {
        for (String email : this.notifier.getEmails()) {
            System.out.println("[이메일 발신]" + email + "\" 로 내용: \"" + message + "\" 을 보냈습니다.");
        }
    }
}
  • 이메일을 보내는데 사용될 데코레이터이다.

SlackNotifierDecorator 생성

public class SlackNotifierDecorator extends NotifierBaseDecorator {
    public SlackNotifierDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public void send(String message) {
        for (String email : this.notifier.getEmails()) {
            System.out.println("[슬랙 메세지 발신]" + email + "\" 로 내용: \"" + message + "\" 을 보냈습니다.");
        }
    }
}
  • 슬랙 메세지를 보내는데 사용될 데코레이터이다.

FacebookNotifierDecorator 생성

public class FacebookNotifierDecorator extends NotifierBaseDecorator {
    public FacebookNotifierDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public void send(String message) {
        for (String email : this.notifier.getEmails()) {
            System.out.println("[페이스북 메세지 발신]" + email + "\" 로 내용: \"" + message + "\" 을 보냈습니다.");
        }
    }
}
  • 페이스북 메세지를 보내는데 사용될 데코레이터이다.

Notifier 코드 수정

public class Notifier implements NotifierInterface {
    private final ArrayList<NotifierInterface> notifiers = new ArrayList<>();
    private final ArrayList<String> emails = new ArrayList<>();

    // 데코레이터 이용 방법 1
    public void decorate(Class<? extends NotifierBaseDecorator> decorator)  {
        if(containNotifier(decorator)) {
            System.out.println("이미 추가된 발신 타입입니다.");
            return;
        }

        Constructor<? extends NotifierBaseDecorator> constructor = null;

        try {
            constructor = decorator.getConstructor(Notifier.class);
            addNotifier(constructor.newInstance(this));
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    // 데코레이터 이용 방법 2
    public void facebookEnabled(boolean bool) {
        if (bool) {
            if(containNotifier(FacebookNotifierDecorator.class)) {
                System.out.println("이미 활성화된 상태입니다.");
                return;
            }

           addNotifier(new FacebookNotifierDecorator(this));
           return;
        }

        removeNotifier(FacebookNotifierDecorator.class);
    }

    // 기타 나머지 코드들...
}
  • 데코레이터 패턴을 적용하기 위해 decorate 메서드와 facebookEnabled 메서드를 시범삼아 구현해보았다.
  • decorate 메서드는 리플렉트를 통한 일반화 방식이고, NotifierBaseDecorator 를 상속한 어떤 클래스든 넣을 수 있다.
  • facebookEnabled 는 유저 친화적인 방식으로 메서드를 구현한 것이다.

Notifier 의 전체 코드

public class Notifier implements NotifierInterface {
    private final ArrayList<NotifierInterface> notifiers = new ArrayList<>();
    private final ArrayList<String> emails = new ArrayList<>();

    public ArrayList<String> getEmails() {
        return emails;
    }

    private void addNotifier(NotifierInterface notifier) {
        notifiers.add(notifier);
    }

    private void removeNotifier(Class<?> notifierClass) {
        NotifierInterface foundNotifier = findNotifier(notifierClass);

        if(foundNotifier != null) {
            notifiers.remove(foundNotifier);
        }
    }

    private NotifierInterface findNotifier(Class<?> notifierClass) {
        for (NotifierInterface notifier : notifiers) {
            if(notifier.getClass().equals(notifierClass)) {
                return notifier;
            }
        }

        return null;
    }

    private boolean containNotifier(Class<?> notifierClass) {
        for (NotifierInterface notifier : notifiers) {
            if(notifier.getClass().equals(notifierClass)) {
                return true;
            }
        }

        return false;
    }

    public void decorate(Class<? extends NotifierBaseDecorator> decorator)  {
        if(containNotifier(decorator)) {
            System.out.println("이미 추가된 발신 타입입니다.");
            return;
        }

        Constructor<? extends NotifierBaseDecorator> constructor = null;

        try {
            constructor = decorator.getConstructor(Notifier.class);
            addNotifier(constructor.newInstance(this));
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void facebookEnabled(boolean bool) {
        if (bool) {
            if(containNotifier(FacebookNotifierDecorator.class)) {
                System.out.println("이미 활성화된 상태입니다.");
                return;
            }

           addNotifier(new FacebookNotifierDecorator(this));
           return;
        }

        removeNotifier(FacebookNotifierDecorator.class);
    }

    public final void addEmail(String email) {
        emails.add(email);
        System.out.println("이메일 \"" + email + "\" 가 성공적으로 수신 목록에 추가되었습니다.");
    }

    @Override
    public void send(String message) {
        if(notifiers.isEmpty()) {
            System.out.println("등록된 NotifierInterface 가 없습니다.");
            return;
        }

        if(emails.isEmpty()) {
            System.out.println("등록된 수신자가 없습니다.");
            return;
        }

        for (NotifierInterface notifier : notifiers) {
            notifier.send(message);
        }
    }
}

Client 코드에서 결과 살펴보기

public class Client {
    public static void main(String[] args) {
        Notifier notifier = new Notifier();

        notifier.addEmail("n00nietzsche@gmail.com");
        notifier.addEmail("billgates@microsoft.com");

        // 사용자 친화적인 방법
        notifier.facebookEnabled(true);

        // 공통화를 쉽게 할 수 있는 방법
        notifier.decorate(EmailNotifierDecorator.class);
        notifier.decorate(SlackNotifierDecorator.class);

        notifier.send("하이.");
    }
}

/*
출력 결과:
이메일 "n00nietzsche@gmail.com" 가 성공적으로 수신 목록에 추가되었습니다.
이메일 "billgates@microsoft.com" 가 성공적으로 수신 목록에 추가되었습니다.
[페이스북 메세지 발신]n00nietzsche@gmail.com" 로 내용: "하이." 을 보냈습니다.
[페이스북 메세지 발신]billgates@microsoft.com" 로 내용: "하이." 을 보냈습니다.
[이메일 발신]n00nietzsche@gmail.com" 로 내용: "하이." 을 보냈습니다.
[이메일 발신]billgates@microsoft.com" 로 내용: "하이." 을 보냈습니다.
[슬랙 메세지 발신]n00nietzsche@gmail.com" 로 내용: "하이." 을 보냈습니다.
[슬랙 메세지 발신]billgates@microsoft.com" 로 내용: "하이." 을 보냈습니다.
*/
  • 출력 결과 주석을 보았을 때, 정상적으로 작동하는 것을 볼 수 있다.
  • 지금은 단순히 메일 전송을 위한 send() 메서드 하나 뿐이라 그냥 한 클래스 안에서 전부 구현해도 크게 복잡하지 않다.
    • 나중에 '푸시알람 보내기', '그룹에 초대하기' 등의 여러가지 기능이 더 생기고 디스코드, 마스토돈, 트위터 등 많은 SNS 가 부가적으로 생겨서 인터페이스에 메서드가 많이 추가된다면, Notifier 하나로는 매우 복잡했을 구현을 손쉽게 가져갈 수 있다.
  • 코드의 복잡도 뿐만 아니라, 일반적인 1차원적 상속과 달리 런타임에 보내는 SNS 목록등을 쉽게 편집할 수 있는 것도 큰 장점이다.

다이어그램 살펴보기

  • 코드는 Pizza 보다 복잡했지만, 다이어그램을 보면 Pizza 예제와 거의 흡사하다.

데코레이터 패턴의 장점과 단점

  • 데코레이터 패턴의 장점과 단점을 알아보자.

장점

  • 데코레이터 (래퍼 클래스) 를 이용해 기능을 조합할 수 있다.
    • 상속을 이용하는 경우엔 '조합'은 불가능하여 유연하지 못했다.
    • 조합의 순서도 상관없이 조합이 가능하다.
  • 컴파일 타임에 기능의 내용이 확정되는 것이 아니라 런타임에 기능의 내용을 변경할 수 있다.
    • 상속의 경우에는 이미 컴파일 타임에 관계가 확정되어 있다.

단점

  • 데코레이터를 조합하는 코드가 조금 복잡할 수 있다.
    • 작은 객체들이 많이 늘어난다.
    • 사실 상속에 비하면 객체들이 상속보다 많이 늘어나진 않아서 큰 단점은 아니다.

스프링에서 사용되는 데코레이터 패턴

  • 스프링에서의 HandlerAdapter 를 데코레이터 패턴의 예로 볼 수 있다.
  • 데코레이터 패턴의 특징은 래퍼 객체를 이용해 기존 객체에 영향을 미치지 않고 새로운 기능을 추가하는 것이라 볼 수 있다.
  • HandlerAdapter 는 스프링에서 특정한 타입의 컨트롤러를 디스패처 서블릿에 사용할 수 있게 해준다.
    • SimpleControllerHandlerAdapter 는 Controller 인터페이스를 구현한 컨트롤러를 사용할 수 있게 해준다.
    • RequestMappingHandlerAdapter 는 @RequestMapping 주석이 달린 컨트롤러를 사용할 수 있게 해준다.
  • 각 HandlerAdapter 들은 기존의 동작을 해치지 않고, 새로운 종류의 컨트롤러가 동작할 수 있도록 도와준다.
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
      if (adapter.supports(handler)) {
        return adapter;
      }
    }
  }
  throw new ServletException("No adapter for handler [" + handler +
      "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
  • HandlerAdapter 를 리스트 형태로 보관하여, 알맞은 handlerAdapter 를 사용할 수 있게 해준다.
    • supports 메서드는 초기 다이어그램에서 Component 인터페이스가 제공하는 부분에 해당한다.
    • HandlerAdapter 인터페이스 자체가 Component 인터페이스 격이다.
  • SimpleControllerHandlerAdapter 혹은 RequestMappingHandlerAdapter 가 Concrete Decorator 로 볼 수 있다.

데코레이터 패턴 vs 어댑터 패턴

  • HandlerAdapter 는 어댑터 패턴에서도 예제로 등장했던 코드이다.
  • 코드를 보는 시선에 따라 어댑터 패턴과 데코레이터 패턴 둘 다 될 수 있다.
  • HandlerAdapter 의 의도를 서로 다른 코드와의 호환 이라고 본다면, 어댑터 패턴이 된다.
  • HandlerAdapter 에 어댑터를 추가하여 기존 객체에 영향을 미치지 않고 새로운 기능을 추가하는 것 이라고 본다면, 데코레이터 패턴이 된다.

자바에서 사용되는 데코레이터 패턴

Collections 의 다양한 정적 메서드들

아래의 Collections 메서드는 Collection 객체 안에 있는 내용이나 기존 기능을 전혀 변경하지 않고도 새로운 기능을 추가할 수 있다.

  • synchronizedList(): Collection 을 thread-safe 하게 만든다.
  • checkedList(): Collection 에 특정 타입만 들어올 수 있게 만든다.
  • unmodifiableList(): Collection 을 더이상 수정될 수 없는 불변으로 만든다.

데코레이터 패턴의 예제인만큼 한 번에 한 가지만 적용할 수 있는게 아니라, 여러가지를 한 Collection 에 적용하는 것도 가능하다.

HttpServletRequestWrapper 혹은 HttpServletResponseWrapper

  • 이름부터 래퍼 객체임을 명확히 알리고 있다.
  • 기본 자바 서블릿 API 인 HttpServletRequest 에서 넘어온 정보를 HttpServletRequestWrapper 래퍼 객체로 감싸 애플리케이션 단에서 입맛에 맞게 수정하기 위한 기능들을 추가해준다.
  • 초기 서블릿 요청에 대한 내용을 편집할 수 있다.
반응형
저작자표시 비영리 (새창열림)

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

플라이웨이트 패턴 (Flyweight Pattern) 이란?  (0) 2023.04.22
퍼사드 패턴 (Facade Pattern) 이란?  (0) 2023.04.18
컴포지트 패턴 (Composite Pattern, 컴포짓 패턴) 이란?  (0) 2023.02.20
브릿지 패턴 (Bridge Pattern) 이란?  (0) 2023.02.19
자바 디자인 패턴, 객체 생성 관련 패턴 (Object Creational Patterns) 이란?  (0) 2023.02.17
    'Java/자바 디자인 패턴' 카테고리의 다른 글
    • 플라이웨이트 패턴 (Flyweight Pattern) 이란?
    • 퍼사드 패턴 (Facade Pattern) 이란?
    • 컴포지트 패턴 (Composite Pattern, 컴포짓 패턴) 이란?
    • 브릿지 패턴 (Bridge Pattern) 이란?
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바