[디자인 패턴] 데코레이터 패턴

Stupefyee's avatar
Nov 13, 2024
[디자인 패턴] 데코레이터 패턴
💡
객체들을 새로운 행동들을 포함한 특수 래퍼 객체들 내에 넣어서 위 행동들을 해당 객체들에 연결시키는 구조적 디자인 패턴

1. 개요

  • 객체의 기능을 동적으로 추가하는 구조적 패턴.
  • 상속 대신 객체를 감싸서 기능을 확장.
  • 기존 객체를 변경하지 않고 기능을 추가할 수 있음.

2. 구성 요소

  • Component: 기능을 추가할 기본 인터페이스.
  • ConcreteComponent기본 객체로, 실제 기능을 구현.
  • Decorator기본 객체를 감싸는 추상 클래스.
  • ConcreteDecorator기능 추가를 위한 구체적 클래스.

3. 장점

  • 유연성: 런타임 중에 기능을 동적으로 추가.
  • 재사용성: 다양한 객체에서 기능을 재사용 가능.
  • 응집성: 기능별로 독립적인 클래스로 관리.

4. 단점

  • 객체 수 증가: 데코레이터가 많으면 객체 수가 증가.
  • 복잡성 증가: 데코레이터가 많을수록 조합을 관리하기 어려움.

5. Java 코드 예시

5.1. Coffee 인터페이스 (Component)

java 코드 복사 // Component: 기본 인터페이스 public interface Coffee { double cost(); // 가격을 반환하는 메서드 }

5.2. SimpleCoffee 클래스 (ConcreteComponent)

java 코드 복사 // ConcreteComponent: 기본 커피 객체 public class SimpleCoffee implements Coffee { @Override public double cost() { return 5.0; // 기본 커피 가격 } }

5.3. CoffeeDecorator 클래스 (Decorator)

java 코드 복사 // Decorator: 기본 객체를 감싸는 추상 클래스 public abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; // 기본 커피 객체 // 생성자를 통해 기본 커피 객체를 설정 public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } @Override public double cost() { return coffee.cost(); // 기본 커피 가격 반환 } }

5.4. MilkDecorator 클래스 (ConcreteDecorator)

java 코드 복사 // ConcreteDecorator: 우유 추가 public class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); // 기본 커피 객체 설정 } @Override public double cost() { return super.cost() + 2.0; // 우유 추가 비용 2.0 더하기 } }

5.5. SugarDecorator 클래스 (ConcreteDecorator)

java 코드 복사 // ConcreteDecorator: 설탕 추가 public class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); // 기본 커피 객체 설정 } @Override public double cost() { return super.cost() + 1.0; // 설탕 추가 비용 1.0 더하기 } }

5.6. 테스트 클래스 (클라이언트 코드)

java 코드 복사 // 테스트 클래스: 데코레이터 패턴 사용 예시 public class DecoratorPatternTest { public static void main(String[] args) { Coffee simpleCoffee = new SimpleCoffee(); System.out.println("Simple Coffee cost: " + simpleCoffee.cost()); // 우유 추가된 커피 Coffee milkCoffee = new MilkDecorator(simpleCoffee); System.out.println("Milk Coffee cost: " + milkCoffee.cost()); // 설탕과 우유가 추가된 커피 Coffee milkSugarCoffee = new SugarDecorator(milkCoffee); System.out.println("Milk and Sugar Coffee cost: " + milkSugarCoffee.cost()); // 설탕, 우유, 기본 커피가 추가된 커피 Coffee fullyDecoratedCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee())); System.out.println("Fully decorated Coffee cost: " + fullyDecoratedCoffee.cost()); } }

5.6. 동작 예시

💡
Simple Coffee cost: 5.0 Milk Coffee cost: 7.0 Milk and Sugar Coffee cost: 8.0 Fully decorated Coffee cost: 8.0

6. 비교: 데코레이터 vs 상속

데코레이터
상속
기능 추가
동적으로 기능 추가
서브클래스를 통해 기능 추가
유연성
기능을 런타임에 조합 가능
컴파일 타임에 고정된 기능
객체 수
객체 수 증가
클래스 수 증가

7. 결론

  • 상속 대신 객체를 감싸서 기능을 동적으로 확장.
  • 다양한 기능을 독립적으로 관리할 수 있어, 유연하고 확장성 있는 코드 작성 가능.
  • 기존 객체를 수정하지 않고 기능을 추가하는 방식으로 응집력을 높이고, 변경에 강한 구조를 제공.
Share article

stupefyee