Design Pattern with Java

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

초코너무조코 2025. 1. 17. 14:16
728x90

 

목차


    1. 데코레이터 패턴이란?

    데코레이터 패턴(Decorator Pattern)은 객체에 동적으로 추가적인 기능을 부여할 수 있는 디자인 패턴입니다. 기본적으로, 데코레이터 패턴은 기존 클래스에 기능을 추가하고 싶을 때 사용됩니다. 이는 상속 대신 구성(Composition)을 사용하여 동적으로 기능을 확장할 수 있게 해줍니다.

    데코레이터 패턴 사용 이유:

    • 객체의 기능을 동적으로 추가할 수 있습니다.
    • 상속을 사용하지 않고 기존 코드 수정 없이 기능 확장이 가능합니다.
    • 여러 기능을 조합하여 사용할 수 있습니다.

    2. 데코레이터 패턴의 특징

    • 기존 클래스를 변경하지 않고 기능을 추가할 수 있습니다.
    • 데코레이터 클래스는 Coffee 객체를 포함하여 추가 기능을 구현합니다.
    • 여러 데코레이터를 체인처럼 연결하여 기능을 확장할 수 있습니다.
    • 유연한 구조로 기능을 쉽게 변경하고 확장할 수 있습니다.

    3. 자바에서 데코레이터 패턴 구현하기

    예시: 커피 주문 시스템

    데코레이터 패턴을 이해하기 위해 커피 주문 시스템을 예시로 들어볼게요. 기본적으로 커피를 주문하면 기본적인 커피가 나오는데, 우유설탕 등을 추가할 수 있습니다. 이때, 각각의 추가 기능을 데코레이터로 구현할 수 있습니다.

    1. 기본 커피 인터페이스

    // 기본 커피 인터페이스
    public interface Coffee {
        String getDescription();
        double cost();
    }
    

    Coffee 인터페이스는 커피의 기본적인 getDescription() 메서드(설명)와 cost() 메서드(가격)를 정의합니다.


    2. 기본 커피 클래스

    // 기본 커피 클래스
    public class SimpleCoffee implements Coffee {
    
        @Override
        public String getDescription() {
            return "Simple Coffee";
        }
    
        @Override
        public double cost() {
            return 5.0; // 기본 커피 가격
        }
    }
    

    SimpleCoffee 클래스는 Coffee 인터페이스를 구현한 기본 커피 클래스입니다. 기본적으로 "Simple Coffee"라는 설명과 5.0의 가격을 반환합니다.


    3. 데코레이터 클래스

    데코레이터 클래스는 Coffee 객체를 래핑하여 그 객체에 기능을 추가합니다.

    // 커피 데코레이터 클래스 (추상 클래스)
    public 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();
        }
    }
    

    CoffeeDecorator 클래스는 Coffee 인터페이스를 구현하며, 실제로 데코레이터가 기능을 추가할 객체를 decoratedCoffee 필드로 포함하고 있습니다. 그 후 getDescription()과 cost() 메서드를 오버라이드하여 기본 커피 기능을 그대로 사용하고, 추가적인 기능을 추가할 수 있게 됩니다.


    4. 기능 추가: 우유와 설탕

    우리는 이제 기본 커피에 우유설탕을 추가하는 데코레이터를 만들겠습니다.

    // 우유 추가 데코레이터
    public 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.0; // 우유 추가 비용
        }
    }
    

    MilkDecoratorCoffeeDecorator 클래스를 상속받아 getDescription()과 cost() 메서드를 오버라이드하여 기본 커피에 Milk를 추가하고, 비용도 추가합니다.

    // 설탕 추가 데코레이터
    public 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; // 설탕 추가 비용
        }
    }
    

    SugarDecorator도 비슷하게 getDescription()과 cost() 메서드를 오버라이드하여 설탕을 추가합니다.


    5. 데코레이터 사용하기

    이제 실제로 데코레이터 패턴을 어떻게 사용할 수 있는지 보겠습니다.

    public class CoffeeShop {
        public static void main(String[] args) {
            Coffee coffee = new SimpleCoffee(); // 기본 커피 주문
    
            System.out.println("Order 1: " + coffee.getDescription() + " Cost: " + coffee.cost());
    
            // 우유 추가
            coffee = new MilkDecorator(coffee);
            System.out.println("Order 2: " + coffee.getDescription() + " Cost: " + coffee.cost());
    
            // 설탕 추가
            coffee = new SugarDecorator(coffee);
            System.out.println("Order 3: " + coffee.getDescription() + " Cost: " + coffee.cost());
        }
    }
    

    위 코드는 기본 커피에서 우유와 설탕을 동적으로 추가하는 방식입니다. 실행 결과는 다음과 같습니다.

    Order 1: Simple Coffee Cost: 5.0
    Order 2: Simple Coffee, Milk Cost: 6.0
    Order 3: Simple Coffee, Milk, Sugar Cost: 6.5
    

    4. 예제 코드 설명

    • SimpleCoffee 클래스는 기본적인 커피를 나타냅니다. "Simple Coffee"라는 설명과 5.0의 가격을 반환합니다.
    • CoffeeDecorator는 데코레이터 패턴의 핵심으로, 다른 데코레이터 클래스를 통해 커피에 기능을 추가할 수 있도록 합니다.
    • MilkDecoratorSugarDecorator는 기본 커피에 우유설탕을 각각 추가하는 클래스입니다.
    • CoffeeShop 클래스에서 커피를 주문하고 데코레이터를 사용하여 동적으로 우유와 설탕을 추가합니다.

    5. 데코레이터 패턴의 장점과 단점

    장점

    1. 기능 확장이 유연합니다. 새로운 기능을 추가하려면 새로운 데코레이터 클래스를 추가하기만 하면 됩니다.
    2. 기존 코드 수정 없이 기능을 확장할 수 있어, 유지보수가 용이합니다.
    3. 여러 데코레이터를 조합하여 다양한 기능을 만들어낼 수 있습니다.

    단점

    1. 데코레이터가 많아지면 클래스의 수가 증가하여 코드가 복잡해질 수 있습니다.
    2. 디버깅이 어려울 수 있습니다. 여러 데코레이터가 중첩되면, 문제를 추적하는 데 시간이 걸릴 수 있습니다.

    6. 결론

    데코레이터 패턴은 객체의 기능을 동적으로 확장할 수 있는 강력한 패턴입니다. 기존 코드를 변경하지 않고 기능을 추가할 수 있어, 유연하고 유지보수에 용이합니다. 다양한 기능을 조합하여 새로운 기능을 만들어낼 수 있기 때문에, 복잡한 시스템에서 매우 유용하게 사용될 수 있습니다.

    자바에서 데코레이터 패턴을 구현할 때는 구성(Composition)을 활용하여 클래스를 조합하는 방식으로 동작합니다. 이를 통해 확장성과 유연성을 얻을 수 있습니다.

     

    728x90