데코레이터 패턴
더 많은 작업
데코레이터 패턴(Decorator Pattern, Wrapper)은 객체를 같은 인터페이스를 가진 감싸개(wrapper) 객체로 겹겹이 둘러싸, 원래 클래스를 고치거나 상속을 늘리지 않고 실행 중에 기능을 덧붙이는 GoF 구조 패턴이다.
기능 조합마다 하위 클래스를 만들면 조합 수만큼 클래스가 불어난다. 예를 들어 알림 기능에 문자·메신저·전자우편 발송을 마음대로 섞으려면 "문자+메신저", "문자+전자우편" 같은 하위 클래스가 조합마다 필요하다. 데코레이터 패턴은 상속 대신 합성을 쓴다. 기본 기능을 가진 객체를 데코레이터가 필드로 품고, 같은 연산을 받으면 품은 객체에 그대로 넘기면서 그 앞이나 뒤에 자기 일을 끼워 넣는다. 데코레이터도 같은 인터페이스를 구현하므로 데코레이터를 다시 데코레이터로 감쌀 수 있고, 클라이언트는 몇 겹이 감싸였는지 모른 채 원래 객체처럼 쓴다.[1]
감마(Erich Gamma) 등은 이 패턴의 의도를 "객체에 책임을 동적으로 덧붙이며, 기능 확장에서 하위 클래스를 만드는 것보다 유연한 대안을 준다"고 정리했다.[2]
| 역할 | 하는 일 |
|---|---|
| Component | 감싸는 쪽과 감싸이는 쪽이 함께 따르는 공통 인터페이스. 연산 Operation() 을 선언한다 |
| ConcreteComponent | 기본 기능을 가진, 감싸이는 실제 객체 |
| Decorator | Component 를 구현(상속)하면서 Component 타입의 필드로 감쌀 대상을 가진다. Operation() 을 받으면 그 필드의 Operation() 을 호출해 위임한다 |
| ConcreteDecorator | Decorator 를 상속해 Operation() 을 재정의한다. 부모(Decorator)의 Operation() 을 호출한 앞이나 뒤에 덧붙일 행위(AddedBehavior)를 실행한다 |
데코레이터 패턴의 클래스 다이어그램에는 다음 세 가지가 함께 나온다.
- ConcreteComponent 와 Decorator 가 모두 Component 를 상속(또는 구현)한다
- Decorator 에서 Component 쪽으로 집합(빈 마름모) 또는 연관 관계가 있다. Decorator 가 Component 하나를 품는다는 뜻이다. 다이어그램 메모에는 흔히
component->Operation();처럼 품은 객체에 위임하는 코드가 붙는다 - ConcreteDecorator 들이 Decorator 를 상속하고, 그중 일부는
Decorator::Operation(); AddedBehavior();처럼 부모 연산을 부른 뒤 자기 행위를 더한다
즉 Component 를 상속하면서 동시에 Component 를 품는 클래스가 있고, 그 하위 클래스가 부가 행위를 가진다면 데코레이터 패턴이다. 클래스 이름이 Decorator 가 아니어도(예: Quest, Wrapper) 이 모양이면 같다.[2]
자바 입출력 라이브러리가 대표적이다. FilterInputStream 은 다른 InputStream 을 필드로 품고 모든 요청을 그 스트림에 넘기는 클래스이며, 그 하위 클래스가 일부 메서드를 재정의하거나 메서드를 더해 기능을 덧붙인다.[3]
InputStream in = new BufferedInputStream( // 버퍼링을 덧붙인다
new FileInputStream("a.txt")); // 기본 기능: 파일 읽기
BufferedInputStream 과 FileInputStream 은 둘 다 InputStream 이므로, 클라이언트는 감싼 객체를 원래 InputStream 처럼 쓴다. 압축·암호화 같은 기능도 같은 방식으로 한 겹씩 더 감싸 조합한다.[1]
| 패턴 | 목적 | 구조의 특징 |
|---|---|---|
| 데코레이터 | 객체에 기능을 덧붙인다 | 같은 인터페이스로 감싸고, 여러 겹 겹칠 수 있다. Component 를 상속하면서 Component 를 품는다 |
| 프록시 | 실제 객체에 대한 접근을 통제한다(지연 생성·권한 확인·원격 호출) | 같은 인터페이스로 대리하지만 보통 한 겹이고, 실제 객체(RealSubject)를 직접 가리킨다 |
| 브리지 | 추상화와 구현을 분리해 두 계층이 따로 확장되게 한다 | 추상화 계층이 구현 계층의 인터페이스(Implementor)를 참조한다. 두 클래스 계층이 나란히 있고, 추상화가 구현 인터페이스를 상속하지는 않는다 |
| 플라이웨이트 | 비슷한 객체를 공유해 메모리를 아낀다 | 공유할 내부 상태와 바깥에서 넘기는 외부 상태를 나누고, 팩토리가 같은 객체를 돌려준다. 감싸기 구조가 없다 |
| 어댑터 | 인터페이스를 바꾼다 | 감싸는 쪽이 원래와 다른 인터페이스를 내놓는다 |
| 컴포지트 | 부분과 전체를 같은 방식으로 다룬다 | 여러 자식을 가진 트리. 데코레이터는 자식이 하나인 사슬이다 |
프록시와 데코레이터는 다이어그램 모양이 비슷해 헷갈린다. 덧붙이는 행위(AddedBehavior)를 가진 하위 클래스가 여럿 있고 감싸는 클래스 자신이 추상 상위 클래스로서 Component 를 품는다면 데코레이터, 실제 객체 하나를 대신해 접근을 통제한다면 프록시로 본다.[2]
- ↑ 1.0 1.1 1.2 「Decorator」, Refactoring.Guru, https://refactoring.guru/design-patterns/decorator, 확인일: 2026-11-05.
- ↑ 2.0 2.1 2.2 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 『Design Patterns: Elements of Reusable Object-Oriented Software』, Addison-Wesley, 1994, Decorator 절.
- ↑ 「Class FilterInputStream」, Java Platform SE 8 API Specification, Oracle, https://docs.oracle.com/javase/8/docs/api/java/io/FilterInputStream.html, 확인일: 2026-11-05.