구조 패턴(Structural Pattern)
데코레이터 패턴은 객체 지향 디자인 패턴 중 하나로, 기존 객체에 새로운 기능을 추가하거나 객체의 행동을 수정하지 않고, 객체의 기능을 동적으로 확장하는 방법을 제공합니다.
이 패턴은 객체의 기능을 각각의 작은 단위로 분리하고, 이 단위들을 조합해서 새로운 기능을 만들어내는 방법을 사용합니다. 이렇게 분리된 각각의 단위를 데코레이터(Decorator)라고 부릅니다.
데코레이터 패턴에서는 데코레이터가 기존 객체와 동일한 인터페이스를 가지며, 데코레이터는 기존 객체를 래핑(Wrapper)합니다. 이 래핑된 객체는 기존 객체의 기능을 그대로 수행하면서, 새로운 기능을 추가하거나 변경할 수 있습니다. 이렇게 데코레이터 패턴은 객체의 기능을 동적으로 확장할 수 있으며, 기존 코드 수정 없이도 새로운 기능을 추가할 수 있도록 합니다.
예를 들어, 다양한 커피 종류에 대한 주문을 처리하는 코드를 작성하고 있다고 가정해봅시다. 이때 데코레이터 패턴을 사용하여, 커피의 종류마다 추가적인 옵션을 제공하는 방법을 구현할 수 있습니다. 예를 들어, 블랙 커피에 설탕을 추가하는 옵션, 아메리카노에 휘핑 크림을 추가하는 옵션 등이 있을 수 있습니다. 각각의 옵션은 데코레이터로 구현되며, 커피 객체를 래핑하여 새로운 옵션을 추가할 수 있도록 합니다.
자바에서 데코레이터 패턴은 다양한 곳에서 사용됩니다. 그 중에서도 대표적인 예시로는 자바의 입출력 API가 있습니다.
InputStream inputStream = new FileInputStream("input.txt");
DataInputStream dataInputStream = new DataInputStream(inputStream);
int value = dataInputStream.readInt();
위 코드에서 'DataInputStream' 클래스는 'FileInputStream' 클래스를 상속받은 'InputStream' 클래스를 데코레이터로 사용합니다. 따라서 'FileInputStream'으로부터 데이터를 읽으면서, 추가적으로 엔디안 변환을 수행하여 정수 값을 읽어올 수 있습니다. 이와 같은 방식으로 'FilterOutputStream' 클래스를 이용하여 출력에 대해서도 데코레이터 패턴을 구현할 수 있습니다.
Decorator 패턴 예제
Diagrams
자바에서 데코레이터 패턴을 구현하는 방법은 크게 두 가지가 있습니다. 첫 번째는 인터페이스를 이용한 방법이고, 두 번째는 추상 클래스를 이용한 방법입니다. 여기서는 추상 클래스를 이용한 방법으로 예시를 들어보겠습니다.
우선, 커피 객체를 나타내는 'Coffee' 클래스를 생성합니다. 이 클래스는 기본적인 커피에 대한 정보를 가지고 있습니다.
public abstract class Coffee {
protected String description;
public String getDescription() {
return description;
}
public abstract int cost();
}
그리고 이제 추가적인 옵션을 나타내는 데코레이터 클래스들을 생성합니다. 예를 들어, 설탕을 추가하는 옵션을 나타내는 'Sugar' 데코레이터 클래스를 생성해봅시다. 이 클래스는 'Coffee' 클래스를 상속받아 기본적인 커피에 대한 정보를 가지고 있으면서, 설탕을 추가하는 기능을 제공합니다.
public abstract class Sugar extends Coffee {
public abstract String getDescription();
}
마찬가지로, 아메리카노에 휘핑 크림을 추가하는 옵션을 나타내는 'WhipCream' 데코레이터 클래스를 생성해봅시다.
public abstract class WhipCream extends Coffee {
public abstract String getDescription();
}
그리고 이제 각각의 커피 종류에 대한 클래스를 생성합니다. 예를 들어, 블랙 커피에 대한 클래스를 'BlackCoffee'라는 이름으로 생성해봅시다.
public class BlackCoffee extends Coffee {
public BlackCoffee() {
description = "Black Coffee";
}
@Override
public int cost() {
return 3000;
}
}
이제 데코레이터를 사용하여 추가적인 옵션을 제공하는 커피를 만들어보겠습니다. 'Sugar' 클래스를 상속받아 설탕을 추가하는 옵션을 제공하는 'SugarCoffee' 클래스를 생성합니다.
public class SugarCoffee extends Sugar {
private Coffee coffee;
public SugarCoffee(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return this.coffee.getDescription() + ", Sugar";
}
@Override
public int cost() {
return this.coffee.cost() + 200;
}
}
마지막으로, 위와 같은 옵션을 제공하는 커피를 하나 더 만들어보겠습니다. 'WhipCream' 클래스를 상속받아 휘핑크림을 추가하는 옵션을 제공하는 'WhipCreamCoffee' 클래스를 생성합니다.
public class WhipCreamCoffee extends WhipCream {
private Coffee coffee;
public WhipCreamCoffee(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return this.coffee.getDescription() + ", WhipCream";
}
@Override
public int cost() {
return this.coffee.cost() + 500;
}
}
그리고 이제 'SugarCoffee'와 'WhipCreamCoffee' 클래스를 이용하여 블랙 커피에 설탕, 휘핑크림을 추가한 새로운 커피를 만들 수 있습니다.
public class Main {
public static void main(String[] args) {
Coffee blackCoffee = new BlackCoffee(); // 블랙커피 주문
Coffee sugarCoffee = new SugarCoffee(blackCoffee); // 블랙커피 + 설탕
Coffee whipCreamCoffee = new WhipCreamCoffee(sugarCoffee); // 블랙커피 + 설탕 + 휘핑크림
System.out.println(whipCreamCoffee.getDescription() + " - " + whipCreamCoffee.cost() + "원");
}
}
실행 결과는 다음과 같습니다.
Black Coffee, Sugar, WhipCream - 3700원
'JAVA > 디자인 패턴' 카테고리의 다른 글
[Design Pattern] Flyweight(플라이웨이트) 패턴이란? (0) | 2023.05.05 |
---|---|
[Design Pattern] Facade(퍼사드) 패턴이란? (0) | 2023.05.05 |
[Design Pattern] Composite(컴포지트) 패턴이란? (0) | 2023.04.30 |
[Design Pattern] Bridge(브릿지) 패턴이란? (0) | 2023.04.30 |
[Design Pattern] Adapter(어댑터) 패턴이란? (0) | 2023.04.30 |