본문 바로가기

언어/Java

[ Java ] Decoration Pattern - 데코레이션 패턴 알아보기

반응형

1. 데코레이터 패턴 (Decorator Pattern)

1. 정의
데코레이터 패턴은 객체에 새로운 기능을 동적으로 추가할 수 있는 디자인 패턴입니다. 기존 객체를 수정하지 않고도 객체에 새로운 행동이나 책임을 부여할 수 있습니다. 이 패턴은 상속을 통한 기능 확장이 아닌, 객체를 감싸는 방식으로 추가적인 기능을 제공하는 방식입니다.

데코레이터 패턴은 주로 다양한 기능을 가진 객체를 유연하게 조합해야 할 때 사용됩니다. 상속을 통해 기능을 확장하는 대신, 객체를 데코레이터로 감싸 여러 기능을 동적으로 추가할 수 있습니다.

2. 목적

  • 유연한 기능 확장: 객체를 감싸서 새로운 기능을 추가할 수 있으므로, 상속 구조보다 더 유연한 방법으로 기능을 확장할 수 있습니다.
  • 기존 클래스의 수정 없이 기능 추가: 데코레이터 패턴은 기존 클래스의 코드를 변경하지 않고도 새로운 기능을 추가할 수 있습니다.
  • 객체의 책임을 동적으로 변경: 실행 중에 객체의 기능을 변경하거나 추가할 수 있기 때문에 매우 유연합니다.

3. 사용 예시
데코레이터 패턴은 다음과 같은 상황에서 유용합니다:

  • 클래스의 기능을 다양한 방식으로 조합할 필요가 있을 때
  • 객체에 대한 기능 확장을 런타임에 동적으로 해야 할 때
  • 기존 객체에 여러 책임을 덧붙여야 하지만, 이를 위해 많은 하위 클래스를 만들고 싶지 않을 때

4. 구현 방법
데코레이터 패턴은 기본적으로 인터페이스나 추상 클래스를 사용해 동일한 타입을 유지하면서, 기능을 확장하는 객체(데코레이터)가 원본 객체를 감싸는 방식으로 구현됩니다. 데코레이터는 원본 객체의 메서드를 호출하면서 그 앞뒤로 새로운 기능을 추가할 수 있습니다.

// 데코레이터 패턴을 구현한 예시

// 1. 컴포넌트 인터페이스
interface Coffee {
    String getDescription();
    double cost();
}

// 2. 기본 컴포넌트 (Concrete Component)
class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple Coffee";
    }

    @Override
    public double cost() {
        return 5.0;
    }
}

// 3. 데코레이터 클래스 (추상 클래스)
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    public double cost() {
        return decoratedCoffee.cost();
    }
}

// 4. 구체적인 데코레이터 클래스 (Concrete Decorators)
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

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

    @Override
    public double cost() {
        return decoratedCoffee.cost() + 1.5;
    }
}

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

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost() + 0.5;
    }
}

public class DecoratorPatternDemo {
    public static void main(String[] args) {
        // 기본 커피
        Coffee coffee = new SimpleCoffee();
        System.out.println(coffee.getDescription() + " $" + coffee.cost());

        // 커피에 우유 추가
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getDescription() + " $" + coffee.cost());

        // 커피에 설탕 추가
        coffee = new SugarDecorator(coffee);
        System.out.println(coffee.getDescription() + " $" + coffee.cost());
    }
}

5. 동작 설명

  • Coffee 인터페이스는 getDescription()cost() 메서드를 정의한 컴포넌트입니다.
  • SimpleCoffee는 기본 커피 클래스이며, getDescription()은 "Simple Coffee"를 반환하고 가격은 $5.0입니다.
  • CoffeeDecorator는 데코레이터 클래스이며, 다른 Coffee 객체를 감쌉니다. 이 클래스는 기본적으로 원본 커피의 기능을 그대로 전달합니다.
  • MilkDecoratorSugarDecorator는 구체적인 데코레이터로, 각각 우유와 설탕을 추가하는 기능을 제공하며, 그에 따른 비용을 추가합니다.

6. 장점

  • 기능 확장의 유연성: 상속을 사용하지 않고 객체의 기능을 추가하거나 변경할 수 있습니다. 데코레이터를 계속해서 조합할 수 있기 때문에 다양한 기능을 동적으로 추가할 수 있습니다.
  • 기존 코드 수정 불필요: 기존 클래스의 코드를 변경하지 않고도 기능을 확장할 수 있으므로, 기존 코드의 안정성을 유지할 수 있습니다.
  • 객체 간 결합도 감소: 데코레이터 패턴은 상속을 사용하지 않으므로 클래스 간 결합도가 낮습니다.

7. 단점

  • 객체가 많아질 수 있음: 데코레이터 패턴을 많이 사용하면 데코레이터 객체가 많이 생길 수 있어 코드가 복잡해질 수 있습니다.
  • 디버깅 어려움: 객체를 여러 데코레이터로 감쌀 경우, 디버깅 시 객체의 구조를 파악하기가 어려울 수 있습니다.
  • 생성 순서가 중요: 데코레이터를 사용하는 순서에 따라 동작이 달라질 수 있기 때문에, 생성 순서를 신경 써야 합니다.

8. 데코레이터 패턴이 유용한 경우

  • 객체의 기능을 동적으로 추가해야 할 때: 런타임에서 객체의 기능을 조합하거나 수정해야 할 때 유용합니다.
  • 여러 조합이 필요한 경우: 여러 기능이 독립적으로 추가될 수 있고, 그 조합에 따라 동작이 달라져야 할 때 데코레이터 패턴이 적합합니다.
  • 상속 대신 기능을 유연하게 확장하고 싶을 때: 상속을 사용하는 대신 객체를 감싸면서 기능을 추가하고 싶을 때 사용됩니다.

9. 실제 사용 예

  • 자바 IO 패키지: 자바의 BufferedReader, InputStreamReader 등은 데코레이터 패턴을 사용하여 기본 입력 스트림에 버퍼링 기능, 문자 변환 기능 등을 동적으로 추가합니다.

데코레이터 패턴은 상속을 사용하지 않고도 객체에 새로운 기능을 동적으로 추가할 수 있는 매우 유용한 패턴으로, 특히 객체의 다양한 기능을 조합하고 싶을 때 적합합니다.

반응형