도찐개찐

[코드디자인패턴-JAVA] Decorator Pattern 본문

프로그래밍/코드디자인패턴

[코드디자인패턴-JAVA] Decorator Pattern

도개진 2024. 2. 29. 09:37

Decorator Pattern

  • 동적으로 추가하거나 수정 할 필요가 있을때 사용하는 구조 패턴.
  • 기본기능에 추가 기능을 연결해 확장성을 향상시키고 코드 중복을 줄일 수 있게 해줍니다.
  • 주로 유연하고 재사용 가능한 코드를 작성 할 때 유용합니다.

사용시점

  • 여러 기능의 조합이 필요 할 때, 여러 기능을 동적으로 추가하거나 제거할 필요가 있을 때.
  • 확장이 예상 되는 경우에, 기능을 쉽게 추가하거나 변경 할 수 있어야 할 때.
  • 기존 클래스를 수정하지 않고 기능을 추가하거나 변경하고 싶을 때.

종류

  1. Concrete Component: 실제 기본 기능을 정의하는 클래스입니다.
  2. Abstract Decorator: 모든 데코레이터가 구현해야 하는 인터페이스나 추상 클래스입니다.
  3. Concrete Decorator: Abstract Decorator를 확장하여, 특정 기능을 추가하는 클래스입니다.
예시:
기본 차량 가격을 계산하는 시스템에서 추가 옵션을 동적으로 추가 할 수 있는 경우 Decorator Pattern이 적합 합니다.
차량은 기본 Component이고, 각각의 추가 옵션(예: 에어컨, 합성 가죽 시트 등)은 Concrete Decorator 입니다.

객체의 확장을 쉽게 만들어 주며, 개방/폐쇄 원칙(Open/closed principle)을 따릅니다.

여기서 "개방"은 확장에 열려있어야 함을 의미하고, "폐쇄"는 기존 코드 수정 없이 확장이 가능해야 함을 의미 합니다.

public interface Beverage {
    String getDescription();
    double cost();
}

1. Concrete Component(기본 기능을 정의하는 클래스)

public class Coffee implements Beverage {
    public String getDescription() {
        return "Coffee";
    }

    public double cost() {
        return 5.0;
    }
}

2. Abstract Decorator(데코레이터가 구현해야 하는 인터페이스)

public abstract class BeverageDecorator implements Beverage {
    protected Beverage beverage;

    public BeverageDecorator(Beverage beverage) {
        this.beverage = beverage;       
    }
}

3. Concrete Decorator(특정 기능을 추가하는 클래스)

public class MilkDecorator extends BeverageDecorator {
    public MilkDecorator(Beverage beverage) {
        super(beverage);
    }

    public String getDescription() {
        return beverage.getDescription() + ", Milk";
    }

    public double cost() {
        return beverage.cost() + 1.0;
    }
}
Beverage beverage = new Coffee();
Beverage milkBeverage = new MilkDecorator(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
System.out.println(milkBeverage.getDescription() + " $" + milkBeverage.cost());
Coffee $5.0
Coffee, Milk $6.0
장점
  1. 유연성: 기존 클래스를 수정하지 않고 기능을 확장할 수 있으므로 유연한 설계가 가능 합니다.
  2. 재사용성: 데코레이터를 여러 객체에 대해 재사용할 수 있습니다.
  3. 개방/폐쇄 원칙을 따름: 기존 코드를 수정하지 않고 새로운 기능을 쉽게 추가할 수 있습니다.
단점
  1. 복잡성 증가: 많은 데코레이터 클래스가 필요한 경우, 코드 복잡도가 높아짐.
  2. 유지보수 어려움 : 데코레이터 체인이 길어질수록 디버깅이나 유지보수가 어려워 질수 있습니다.
데이터 패턴은 객체의 책임을 동적으로, 투명하게 추가할 때 사용되며, 장식이 필요한 다양한 경우에 적용할 수 있습니다.

대표 사례

  1. 자바의 I/O 클래스: 자바의 java.io 패키지에서는 데코레이터 패턴이 광범위하게 사용됩니다. 예를 들어, BufferedReader, BufferedWriter 등의 클래스는 기본 입력/출력 스트림에 버퍼링 기능을 추가하는 데코레이터 역할을 합니다. 다양한 데코레이터를 조합해 원하는 기능을 효율적으로 확장할 수 있습니다.
  2. GUI 컴포넌트: 그래픽 사용자 인터페이스(GUI) 라이브러리에서 데코레이터 패턴은 컴포넌트에 여러 가지 시각적 효과나 동작을 추가할 때 사용될 수 있습니다. 예를 들어, 스크롤 바, 테두리, 그림자 등의 요소를 독립적으로 추가하거나 제거할 수 있습니다.
  3. 웹 서버의 미들웨어: 웹 서버에서 요청과 응답을 처리하는 미들웨어에서도 데코레이터 패턴이 사용될 수 있습니다. 로깅, 인증, 압축, 캐싱 등의 기능을 각각의 데코레이터로 구현하면, 필요한 기능을 조합해 사용할 수 있으며 유지보수도 쉬워집니다.
  4. 커피 샵 메뉴 시스템: 데코레이터 패턴을 설명할 때 자주 사용되는 예시로, 커피나 다른 음료에 다양한 추가 토핑을 조합할 수 있는 시스템을 만들 때 적용할 수 있습니다. 각 토핑을 데코레이터로 구현하면, 다양한 조합의 메뉴를 효율적으로 관리할 수 있습니다.
728x90
Comments