구조 - 2. Decorator

mskimdev·2026년 5월 19일

Design Pattern

목록 보기
7/13

Decorator 패턴

커피를 주문할 때 "아메리카노에 샷 추가, 휘핑 추가"라고 한다. 기본 커피 위에 옵션을 얹는 구조다. 클래스 상속으로 이걸 표현하면 아메리카노샷추가, 아메리카노휘핑추가, 아메리카노샷추가휘핑추가 같은 조합 폭발이 생긴다.

Decorator는 상속 대신 감싸는 방식으로 기능을 덧붙이는 패턴이다.


Decorator 패턴이란

기존 객체를 다른 객체로 감싸서(wrap), 기존 코드는 건드리지 않고 기능을 동적으로 추가하는 패턴이다. 감싸는 객체(데코레이터)는 원본과 동일한 인터페이스를 구현하기 때문에, 사용하는 쪽은 원본인지 데코레이터인지 알 필요가 없다.


구조

// 공통 인터페이스
public interface Coffee {
    String getDescription();
    int getCost();
}

// 기본 구현체
public class Americano implements Coffee {
    @Override
    public String getDescription() { return "아메리카노"; }

    @Override
    public int getCost() { return 3000; }
}
// 데코레이터 베이스 — Coffee를 감싸면서 동일 인터페이스 구현
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

// 샷 추가 데코레이터
public class ShotDecorator extends CoffeeDecorator {
    public ShotDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + " + 샷";
    }

    @Override
    public int getCost() {
        return coffee.getCost() + 500;
    }
}

// 휘핑 추가 데코레이터
public class WhipDecorator extends CoffeeDecorator {
    public WhipDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + " + 휘핑";
    }

    @Override
    public int getCost() {
        return coffee.getCost() + 700;
    }
}
Coffee order = new Americano();
order = new ShotDecorator(order);
order = new WhipDecorator(order);

System.out.println(order.getDescription()); // 아메리카노 + 샷 + 휘핑
System.out.println(order.getCost());        // 4200

데코레이터를 겹겹이 쌓는 구조다. 순서를 바꾸거나 원하는 것만 선택해서 조합할 수 있다.


Java I/O가 Decorator 구조다

Java의 I/O 스트림이 바로 Decorator 패턴으로 설계되어 있다.

// FileInputStream을 BufferedInputStream으로 감싸고
// 다시 DataInputStream으로 감싸는 구조
DataInputStream in = new DataInputStream(
    new BufferedInputStream(
        new FileInputStream("data.bin")
    )
);

FileInputStream이 기본 구현체고, BufferedInputStreamDataInputStream이 데코레이터다. 각각 버퍼링, 타입별 읽기 기능을 추가한다.


상속과의 차이

구분상속Decorator
기능 추가 시점컴파일 타임 (고정)런타임 (동적)
조합 방식클래스 수가 폭발적으로 늘어남객체를 감싸는 방식으로 자유롭게 조합
기존 코드 변경슈퍼클래스 변경 시 영향 받음원본 객체 변경 없음

언제 쓰는가

  • 기능 조합이 다양하고, 상속으로 표현하면 클래스가 너무 많아질 때
  • 런타임에 기능을 동적으로 추가하거나 제거해야 할 때
  • 기존 클래스를 수정하지 않고 기능을 확장해야 할 때

Decorator는 객체를 감쌀수록 기능이 쌓인다. 상속 계층을 늘리는 대신, 필요한 기능만 골라 씌우는 방식으로 훨씬 유연하게 확장할 수 있다.

profile
<- 개발 공부하는 나

0개의 댓글