데코레이터 패턴
- 객체의 기능을 동적으로 확장할 수 있게 해주는 구조적인 디자인 패턴
- 상속이 아닌 위임을 통해 런타임에 부가 기능을 추가하는 것이 가능해짐
주요 구성 요소
1. Component
- 데코레이터와 실제 객체의 공통 인터페이스 혹은 추상 클래스
- ex) Shape 인터페이스
2. ConcreteComponent
- 기본 기능을 구현하는 실제 객체
- ex) Circle, Rectangle 클래스
3. Decorator
- Component 인터페이스를 구현하거나 상속받는 추상 클래스
- 해당 클래스는 실제 객체에 대한 참조를 포함(Dependecy Injection)하며, Component 인터페이스의 메서드를 위임(delegation)
- ex) ShapeDecorator
4. ConcreteDecorator
- Decorator 클래스를 확장하여 추가적인 기능을 구현한 클래스
- 해당 클래스는 Component의 메서드를 호출하여 기본 기능을 유지한 상태로 추가적인 기능을 구현
- ex) RedShapeDecorator
데코레이터 패턴 vs 컴포짓 패턴
- 객체 구조를 다루는 디자인 패턴이라는 점에서 컴포짓 패턴과 유사하지만 목적과 사용 방식에 따라 차이점 존재
데코레이터 패턴 | 컴포짓 패턴 | ||
유사한 점 |
1. 두 패턴 모두 객체의 구조를 정의하고 조작하는 것에 중점을 둠 2. 두 패턴 모두 재귀적으로 객체를 구성하는 방식으로 동작하기 때문에 트리 구조를 형성 3. 새로운 기능을 추가하거나 변경하는 데 있어 매우 유연하며 객체를 동적으로 구성할 수 있어 상속을 통한 확장보다 유연함 4. 두 패턴 모두 공통 인터페이스를 사용하여 객체를 다루기 때문에 클라이언트 코드가 특정 구현 클래스에 의존하지 않고도 동작할 수 있음 |
||
차이점 |
목적 | 객체에 새로운 기능을 동적으로 추가하는 것이 주 목적이며 기본 객체에 여러 데코레이터를 겹쳐 씌우면서 기능을 확장 | 객체를 트리 구조로 구성하여 부분-전체 계층 구조를 표현하는 것이 주 목적이며 이를 통해 개별 객체와 복합 객체를 동일하게 다룰 수 있음 |
구조 | 데코레이터는 기본 객체를 래핑(wrapping)하여 기능을 추가하며 데코레이터는 같은 인터페이스를 구현하며, 내부적으로 실제 객체를 참조 | 컴포짓은 단일 객체와 복합 객체를 구성 요소로 취급하기 때문에 복합 객체는 자신의 자식들을 포함하며, 자식들도 다시 복합 객체일 수 있음 | |
use case | 데코레이터 패턴은 객체에 여러 기능을 동적으로 추가하고 싶을 때 사용 | 컴포짓 패턴은 계층적 구조를 표현하고 클라이언트가 단일 객체와 복합 객체를 동일하게 처리해야할 때 사용 |
데코레이터 패턴 구현 예시
1. Shape 인터페이스 (Component)
- 기본 도형을 나타내는 인터페이스로 draw 메서드를 정의
2. Circle, Rectangle 클래스 (CompositeComponent)
- Shape 인터페이스를 구현하는 구체적인 클래스들
3. ShapeDecorator 클래스 (Decorator)
- Shape 인터페이스를 구현하는 추상 클래스
- Shape 타입의 객체를 포함하고 있으며, draw() 메서드를 구현하여 내부 Shape 객체의 draw() 메서드를 호출
4. RedShapeDecorator 클래스 (ConcreteDecorator)
- ShapeDecorator 클래스를 상속받아 구체적인 데코레이터 클래스를 구현
- draw() 메서드를 재정의하여 기본 Shape의 draw() 메서드를 호출하고, 추가적으로 빨간 테두리를 그리는 기능을 추가
5. Client 코드
- 데코레이터 패턴을 사용하여 도형(Shape) 객체를 생성하고, 필요에 따라 색상 데코레이터(RedShapeDecorator)를 적용하는 클라이언트 코드
- circle과 isColorRed라는 두 개의 boolean 변수를 사용하여 어떤 도형을 생성할지와 해당 도형에 빨간색 테두리를 적용할지 여부를 결정
데코레이터 패턴 장단점
장점
- 데코레이터 패턴은 각 클래스가 하나의 책임만 가지도록 설계 가능하도록 지원
- 기본 클래스는 기본 기능만을 담당하고, 데코레이터 클래스는 추가 기능을 담당
- 이를 통해 클래스의 책임이 명확하게 분리 (SOLID의 SPR 원칙)
- 데코레이터 패턴을 사용하면 클래스의 기존 코드를 변경하지 않고도 새로운 기능 추가 가능 (SOLID의 OCP 원칙)
- 데코레이터와 기본 객체는 동일한 인터페이스를 구현하므로, 데코레이터가 기본 객체로서 대체 가능 (SOLID의 LSP 원칙)
단점
- 남용하거나 지나치게 복잡하게 사용할 경우, 코드의 복잡성을 증가시켜 유지보수와 관리가 어려워질 수 있음
실무에서 쓰이는 데코레이터 패턴
1. 자바 컬렉션
- Collections.checkedList는 런타임에 제네릭 타입을 확인하는 기능을 추가하며 이를 통해 컬렉션에 잘못된 타입의 객체가 추가되는 것을 방지
- 기본 컬렉션 객체를 감싸서 타입 검사를 추가하며 실제 작업은 내부의 기본 컬렉션 객체에 위임되며, 추가적으로 타입 검사가 수행
- Collections.unmodifiableList는 컬렉션을 수정할 수 없도록 만드는 기능을 추가합니다. 이를 통해 컬렉션을 읽기 전용으로 사용 가능
- 기본 컬렉션 객체를 감싸서 수정 작업을 방지
- 모든 읽기 작업은 내부의 기본 컬렉션 객체에 위임되며, 쓰기 작업은 예외를 발생시킴
2. HttpServletRequestDecorator, HttpServletHttpResponseDecorator
- Spring WebFlux의 ServletHttpRequestDecorator와 ServletHttpResponseDecorator는 데코레이터 패턴을 사용하여 HTTP 요청 및 응답 객체에 추가 기능 제공
- 이 데코레이터들은 원래의 HTTP 요청 및 응답 객체를 래핑 하여 기능을 확장하거나 수정할 수 있도록 지원
부연 설명
- HTTP 요청과 응답에 특정 로깅 기능을 추가하는 데코레이터를 사용하여 WebFilter를 구현
참고
코딩으로 학습하는 GoF의 디자인 패턴 - 백기선 강사님
반응형
'Design Pattern' 카테고리의 다른 글
[디자인 패턴] 플라이웨이트 패턴 (Flyweight Pattern) (0) | 2024.06.29 |
---|---|
[디자인 패턴] 퍼사드 패턴 (Facade Pattern) (0) | 2024.06.29 |
[디자인 패턴] 컴포짓 패턴 (Composite Pattern) (0) | 2024.06.22 |
[디자인 패턴] 브릿지 패턴 (Bridge Pattern) (0) | 2024.06.22 |
[디자인 패턴] 어댑터 패턴 (Adapter Pattern) (0) | 2024.06.22 |