데코레이터 패턴(Decorator Pattern)은 구조 패턴 중 하나로 이는 소프트웨어에서 특정한 문제를 해결하기 위해 반복적으로 발생하는 구조를 식별하고, 그 구조에 따라 일반화된 해결책을 제시하는 것을 의미합니다.
한마디로 일괄적으로 관리할 수 있도록 만드는 패턴입니다.
데코레이터 패턴이란?
데코레이터 패턴(Decorator Pattern)은 객체 지향 디자인 패턴 중 하나로 객체에 동적으로 기능을 추가하여 확장할 수 있는 구조 패턴이다. 이 패턴은 상속을 통해 클래스를 확장하는 대신, 객체를 감싸는 방식을 사용하여 기능을 추가하거나 변경한다. 따라서 기존 코드를 수정하지 않고도 새로운 기능을 추가하거나 수정할 수 있게 된다.
한만디로 데코레이터(Decorator)라는 단어의 의미인 장식자, 꾸미는 사람으로 접근하면 데코레이터 패턴의 역할을 쉽게 이해할 수 있을 것이다.
데코레이터 구조

기본 기능을 정의하는 인터페이스로 데코레이터와 구체적인 컴포넌트가 이를 구현한다. 따라서 컴포넌트는 원본 객체와 데코레이터를 묶는 역할을 한다.
기본 기능을 실제로 구현하는 구체적인 컴포넌트로 데코레이터 패턴의 시작점이 된다.
컴포넌트를 감싸는 추상 클래스나 인터페이스로 새로운 기능을 추가하거나 기존 기능을 수정하기 위한 메서드를 선언한다. 데코레이터는 Component를 멤버 변수로 가진다.
새로운 기능이나 기존 기능을 수정하는 역할을 하는 구체적인 클래스다. 데코레이터 인터페이스를 구현한다.
데코레이터 패턴 코드
public interface Coffee {
String getCoffee();
int price();
}
public class Americano implements Coffee {
@Override
public String getCoffee() {
return "아메리카노";
}
@Override
public int price() {
return 2000;
}
}
데코레이터 패턴(미적용)
// 아메리카노에 샷 추가
public class AmericanoAddShot implements Coffee {
private Coffee americano;
public AmericanoAddShot(Coffee americano) {
this.americano = americano;
}
@Override
public String getCoffee() {
return americano.getCoffee() + " 샷추가";
}
@Override
public int getPrice() {
return americano.getPrice() + 500;
}
}
// 아메리카노에 설탕 추가
public class AmericanoAddSugar implements Coffee {
private Coffee americano;
public AmericanoAddSugar(Coffee americano) {
this.americano = americano;
}
@Override
public String getCoffee() {
return americano.getCoffee() + " 설탕추가";
}
@Override
public int getPrice() {
return americano.getPrice() + 500;
}
}
public class DecoratorMain {
public static void main(String[] args) {
Coffee americano = new Americano();
// 아메리카노 샷 추가
Coffee americanoAddShot = new AmericanoAddShot(americano);
System.out.println(americanoAddShot.getCoffee());
System.out.println(americanoAddShot.getPrice());
// 아메리카노 샷, 설탕 추가
Coffee americanoAddSugar = new AmericanoAddSugar(americano);
System.out.println(americanoAddSugar.getCoffee());
System.out.println(americanoAddSugar.getPrice());
}
}
// Decorator
public abstract class CoffeeDecorator implements Coffee {
private Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getCoffee() {
return coffee.getCoffee();
}
@Override
public int getPrice() {
return coffee.getPrice();
}
}
// ConcreteDecoratorA
public class ShotDecorator extends CoffeeDecorator {
public ShotDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getCoffee() {
return super.getCoffee() + " 샷추가";
}
@Override
public int getPrice() {
return super.getPrice() + 500;
}
}
// ConcreteDecoratorB
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getCoffee() {
return super.getCoffee() + " 설탕추가";
}
@Override
public int getPrice() {
return super.getPrice() + 500;
}
}
public class Main {
public static void main(String[] args) {
Coffee americano = new Americano();
Coffee americanoShotAdd = new ShotDecorator(americano);
Coffee americanoShotSugarAdd = new SugarDecorator(americanoShotAdd);
// 아메리카노 샷 추가
System.out.println(americanoShotAdd.getCoffee());
System.out.println(americanoShotAdd.getPrice());
// 아메리카노 샷, 설탕 추가
System.out.println(americanoShotSugarAdd.getCoffee());
System.out.println(americanoShotSugarAdd.getPrice());
}
}
데코레이터 패턴은 기존 객체를 수정하지 않고도 새로운 기능을 추가하거나 변경할 수 있도록 하는 패턴이다. 클라이언트 코드는 데코레이터와 컴포넌트를 동일한 인터페이스로 다룰 수 있어 코드의 일관성을 유지하면서도 다양한 기능을 적용할 수 있다.
추가로 앞서 설명한 예제의 구조를 살펴보면, 단일 책임 원칙(SRP), 개방 폐쇄 원칙(OCP)과 의존성 역전 원칙(DIP)을 준수하고 있다.
데코레이터 패턴 (Decorator Pattern) 이란?
[Java] 데코레이터 패턴(Decorator Pattern)이란? - 개념 및 예제