자바 디자인 패턴 용어 사전
구조 패턴데코레이터 · 래핑

Decorator

객체를 감싸 기능을 덧붙이는 패턴. 상속 대신 합성으로 조합 폭발을 피한다.

같은 인터페이스로 객체를 감싸 기능을 덧붙이는 패턴.

interface DataSource { void write(String data); }

class FileDataSource implements DataSource { ... }

abstract class Decorator implements DataSource {
    protected final DataSource wrappee;
    Decorator(DataSource d) { this.wrappee = d; }
}

class Encryption extends Decorator {
    public void write(String data) { wrappee.write(encrypt(data)); }
}
class Compression extends Decorator {
    public void write(String data) { wrappee.write(compress(data)); }
}
// 런타임에 자유롭게 조합한다
DataSource ds = new Encryption(new Compression(new FileDataSource("a.txt")));

왜 상속이 아닌가 — 조합 폭발

기능 3개를 상속으로 조합하면

  • 암호화 · 압축 · 로깅 · 암호화+압축 · 암호화+로깅 · 압축+로깅 · 셋 다
  • 클래스 7개. 기능이 하나 늘면 15개

데코레이터는 클래스 3개로 모든 조합을 만든다

자바 IO가 교과서다

new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")))
// 파일 읽기 + 문자 변환 + 버퍼링을 각각의 데코레이터로 쌓았다

순서가 의미를 바꾼다

  • Encryption(Compression(file)) — 압축한 뒤 암호화 → 압축률이 좋다

  • Compression(Encryption(file)) — 암호화한 뒤 압축 → 암호문은 압축이 거의 안 된다

  • 같은 부품인데 순서로 결과가 달라진다 — 문서화가 필요한 지점이다

Proxy와 무엇이 다른가

  • 형태는 거의 같다 (같은 인터페이스로 감싼다). 의도가 다르다

  • Decorator — 기능을 '더한다'. 여러 개를 쌓는 것이 전제다

  • Proxy — 접근을 '제어한다'. 대개 하나만 감싼다 (지연 로딩 · 권한 · 캐싱 · 원격)

남용의 경계

  • 겹이 깊어지면 디버깅이 어렵다 (스택 트레이스가 길어진다)

  • 어느 겹에서 문제가 생겼는지 추적이 힘들다

  • 조합이 실제로 여러 가지 필요할 때만 값을 한다

  • 조합이 늘 고정이라면 그냥 한 클래스가 낫다

면접 함정

  • "Decorator는 상속의 대체" → 상속을 쓰긴 한다(같은 인터페이스를 구현). 핵심은 합성으로 조합한다는 점이다.
  • "Proxy와 Decorator는 같다" → 구조가 같고 의도가 다르다.

스프링·자바에서 만나는 형태

// 컬렉션 래핑
Collections.unmodifiableList(list)     // 수정 불가 데코레이터
Collections.synchronizedList(list)     // 동기화 데코레이터

// HTTP 클라이언트 인터셉터 체인도 같은 발상이다
client.newBuilder()
    .addInterceptor(new LoggingInterceptor())
    .addInterceptor(new AuthInterceptor())
    .build();

함수 합성으로도 표현된다

Function<String, String> pipeline =
    ((Function<String, String>) this::compress).andThen(this::encrypt);

데코레이터가 하는 일이 결국 "감싸서 앞뒤에 무언가를 더하는 것" 이라, 함수형에서는 고차 함수로 자연스럽게 표현된다.

인터페이스가 크면 부담이 된다

데코레이터는 감싸는 인터페이스의 모든 메서드를 구현해야 한다
  → 메서드가 20개면 데코레이터마다 20개를 위임해야 한다
  → 추상 데코레이터 클래스를 두어 기본 위임을 한 번만 쓴다
  → 코틀린은 by 키워드로 이 위임이 한 줄이다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 구조 패턴 ① — 맞추고, 나누고, 쌓는다