본문 바로가기

STUDY/# java design pattern

Java Design Pattern - Decorator Pattern

반응형

[Design Pattern]

자바 데코레이터 패턴 (JAVA Decorator Pattern) 

by commin

 

Decorator Pattern 이란?

Decorator Pattern은 객체 지향 디자인 패턴 중 하나로, 기존 클래스의 기능을 동적으로 확장하거나 수정할 수 있게 해주는 패턴입니다. 이 패턴은 Open-Closed Principle (OCP)에 부합하며, 클래스를 수정하지 않고도 기능을 추가하거나 수정할 수 있도록 합니다.

Decorator Pattern은 일반적으로 다음과 같은 상황에서 사용됩니다.

  • 기존 클래스를 수정하지 않고 기능을 확장해야 하는 경우
  • 동적으로 객체의 기능을 추가하거나 제거해야 하는 경우
  • 기존 클래스에 대한 수정이 어려운 경우 (예: 라이브러리 클래스)

Decorator Pattern의 구성요소는 다음과 같습니다.

  1. Component: Decorator가 구현할 인터페이스 또는 추상 클래스입니다.
  2. ConcreteComponent: 구체적으로 구현된 클래스입니다. Decorator 패턴에서 수정하려는 클래스입니다.
  3. Decorator: Component와 동일한 인터페이스를 구현하는데, 실제로 Component에 추가 기능을 제공하는 클래스입니다. 이 클래스는 자신이 장식하고 있는 Component를 참조합니다.
  4. ConcreteDecorator: Decorator를 상속하며, 추가적인 기능을 제공하는 클래스입니다.

Decorator Pattern 예시 코드

이제 Decorator Pattern을 예제를 통해 살펴보겠습니다.

예제: 커피 주문 시 주문한 커피에 대해 추가적인 토핑을 선택할 수 있는 시스템

먼저, Component 인터페이스를 정의합니다.

public interface Coffee {
    String getDescription();
    double getCost();
}

다음으로 ConcreteComponent인 Espresso 클래스를 정의합니다.

public class Espresso implements Coffee {
    @Override
    public String getDescription() {
        return "Espresso";
    }

    @Override
    public double getCost() {
        return 3000;
    }
}

이제 Decorator 클래스를 구현합니다. 이 클래스는 추가적인 기능을 제공하는 클래스로, Coffee를 구현합니다.

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 double getCost() {
        return coffee.getCost();
    }
}

 

마지막으로 ConcreteDecorator를 구현합니다. 이 클래스는 CoffeeDecorator를 상속하며, 추가적인 기능을 제공합니다.

 

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

    @Override
    public String getDescription() {
        return coffee.getDescription() + ", Whipped Cream";
    }

    @Override
    public double getCost() {
        return coffee.getCost() + 500;
    }
}

이제, 다음과 같이 커피 주문을 처리할 수 있습니다.

Coffee espresso = new Espresso();
System.out.println("Order: " + espresso.getDescription() + ", cost: " + espresso.getCost());

Coffee whippedCreamEspresso = new WhippedCream(espresso);
System.out.println("Order: " + whippedCreamEspresso.getDescription() + ", cost: " + whippedCreamEspresso.getCost());

위 코드에서는 먼저 Espresso 객체를 생성하여 주문하고, 그 다음으로 Espresso 객체에 WhippedCream 토핑을 추가하여 주문합니다.

실행 결과는 다음과 같습니다.

Order: Espresso, cost: 3000
Order: Espresso, Whipped Cream, cost: 3500

WhippedCream 클래스는 CoffeeDecorator 클래스를 상속하여 구현되었으며, Espresso 객체를 참조합니다. WhippedCream 클래스에서는 Espresso 객체의 getDescription() 메서드를 호출하여 "Espresso"를 반환하고, getCost() 메서드를 호출하여 3000을 반환합니다. 그리고, WhippedCream 클래스에서 추가적인 getDescription() 메서드와 getCost() 메서드를 구현하여 "Espresso, Whipped Cream"을 반환하고 500를 반환합니다. 따라서, WhippedCream 객체의 getCost() 메서드에서는 3000 + 500 = 3500을 반환합니다.

이와 같이 Decorator Pattern은 기존 클래스를 수정하지 않고도 객체의 기능을 확장하거나 수정할 수 있도록 합니다. 이 패턴은 유연성과 확장성을 높여주며, OCP를 준수하는 객체 지향 설계를 가능하게 합니다.

 


Open-Closed Principle (OCP)은 객체 지향 프로그래밍의 5대 원칙(SOLID) 중 하나로, 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려있어야 하고, 수정에는 닫혀있어야 한다는 원칙입니다.

즉, 기존 코드를 변경하지 않고도 기능을 추가하거나 변경할 수 있어야 한다는 것을 의미합니다. 이를 통해 소프트웨어 설계를 유연하게 만들어 변경과 확장에 용이하게 합니다.

OCP를 준수하는 코드는 다음과 같은 특징을 가집니다.

  • 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다.
  • 코드의 확장성이 높아집니다.
  • 코드의 유지보수가 용이해집니다.
  • 코드의 재사용성이 높아집니다.

하지만, OCP를 준수하는 것이 항상 가능한 것은 아닙니다. 모든 상황에서 설계를 완벽하게 하기는 어렵기 때문에 OCP를 위반해야 하는 경우도 있습니다. 그러나, OCP를 지키지 않는 경우 코드의 유지보수 및 개선이 어려워질 수 있습니다.

반응형