학습 목표

  • 객체의 기능 확장을 상속이 아닌 합성(Composition)으로 해결하는 방식을 이해한다.
  • OCP(개방-폐쇄 원칙) — 기존 코드를 변경하지 않고 기능을 확장하는 원칙을 실전에서 적용한다.
  • 런타임에 동적으로 기능 추가가 필요한 상황에서 데코레이터를 적용할 수 있게 한다.
  • 프레임워크에서 **데코레이터 사용 사례(Spring 필터, InputStream, OutputStream)**를 이해한다.

활용 예제

  • 커피 주문 시스템에서 고객이 샷 추가, 우유 추가, 시럽 추가 등을 선택할 수 있는 경우
  • HTTP 요청 처리 필터 체인
  • 로그 처리에 타임스탬프, 라벨, 파일 저장 기능을 겹겹이 추가하는 구조

예제

더보기

Coffee에서 에스프레소와 밀크를 추가하는 거에 따른 가격 변동을 확인

  • 컴포넌트 기본 인터페이스
public interface Coffee {

    String getDescription();
    int getCost();
}
  • 기본 구현 클래스
public class BasicCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Basic Coffee";
    }

    @Override
    public int getCost() {
        return 3000;
    }
}
  • 데코레이터 추상 클래스
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

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

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

    @Override
    public int getCost() {
        return coffee.getCost();
    }
}
  • 구체적인 데코레이터
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Milk";
    }

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

public class ShotDecorator extends CoffeeDecorator {
    public ShotDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Extra Shot";
    }

    @Override
    public int getCost() {
        return super.getCost() + 700;
    }
}
  • 구현
public class Main {
    public static void main(String[] args) {
        Coffee coffee = new BasicCoffee();
        coffee = new MilkDecorator(coffee);
        coffee = new ShotDecorator(coffee);

        System.out.println(coffee.getDescription()); // Basic Coffee, Milk, Extra Shot
        System.out.println(coffee.getCost());        // 4200
    }
}

- 기본 coffee를 생성해서 우유를 추가하고, 샷을 추가하는 과정을 나타내며 coffee를 체인으로 계속 가져가면서 가격이 상승하는 효과가 있다.

마무리

데코레이터 패턴은 기능 확장에 유리하다는 장점이 있는 것 같고, Lombok에 @Builder가 해당 패턴을 이용해서 만들어졌다고 합니다. 이렇든 확장설계가 필요할 때 고려해 주면 좋을 것 같습니다.

+ Recent posts