본문 바로가기
객체 지향 프로그래밍/디자인 패턴

[구조 패턴] 데코레이터 패턴 (Decorator Pattern) 이란?

by xangmin 2022. 5. 26.
반응형

데코레이터 패턴 이란?

객체의 결합을 통해 기능을 동적으로 유연하게 확장수 있게 하는 패턴

주어진 상황 및 용도에 따라 어떤 객체에 책임(기능)을 동적으로 추가하는 패턴

클래스의 요소들을 계속해서 수정하면서 사용하는 구조에 적합

 

장점 단점
기존 코드를 수정하지 않고 확장 가능 의미 없는 객체들이 추가될 수 있음
구성과 위임을 통해 실행 중 새로운 행동 추가 가능 너무 많은 사용은 코드의 복잡성 증가

 

데코레이터 패턴 구조

Component 실질적인 인스턴스를 컨트롤하는 역할
ConcreteComponent Component의 실질적인 인스턴스의 부분으로 책임의 주체의 역할
Decorator ComponentConcreteDecorator를 동일시 하도록 해주는 역할
ConcreteDecoreator 실질적인 장식 인스턴스 및 정의이며 추가된 책임의 주체

 

사용 예시

커피 주문 프로그램을 만든다고 생각해보자. 당연히 커피 종류가 정의되어야 한다.

Q) 다음 구조에서 커피에 두유모카 토핑을 하려면 어떻게 해야 할까?

 

Solution 1) 조합 가능한 경우의 수를 모두 서브 클래스로 정의한다. (토핑을 추가할 수록 더 많은 서브 클래스 생성)

 

Solution 2) Beverage에서 토핑에 대한 Boolean 변수를 추가하는 것이다. Decaf를 주문했을 때 mocha를 추가했다면 mocha = True셋팅한다. 이 방법의 단점은 우유 같은 새로운 토핑이 추가될 때 Beverage 코드를 수정해야한다. (OCP 위배)

 

Solution 3) 커피와 토핑을 분리하는 구조. 토핑은 CondimentDecorator(재료) 클래스를 상속 받는다. , CondimentDecoratorBeverage 클래스를 상속받는다.

 

 

public abstract class Beverage {
    String description = "제목 없음";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}
public class Decaf extends Beverage{
    public Decaf() {
        description = "디카페인 커피";
    }

    public double cost() {
        // 가격은 1.05달러
        return 1.05;
    }
}
public class Soy extends CondimentDecorator {
    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription()+", 두유";
    }

    @Override
    public double cost() {
        return .15 + beverage.cost();
    }
}
public class SteamMilk extends CondimentDecorator{
    Beverage beverage;

    public SteamMilk(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription()+",스팀밀크";
    }

    @Override
    public double cost() {
        return .10 + beverage.cost();
    }
}
 public abstract class CondimentDecorator extends Beverage{

     public abstract String getDescription();
 }
public class StarBuzzCoffee {

    public static void main(String[] args) {
        Beverage decaf;
        decaf = new Decaf();
        decaf = new Soy(decaf);
        decaf = new Soy(decaf);
        decaf = new SteamMilk(decaf);

        // 토핑을 얹은 디카페인 커피 정보
        System.out.println(decaf.getDescription()
                       + “ $" + decaf.cost());
    }

}

실행결과

디카페인 커피, 두유, 두유, 스팀밀크 $1.45

 

 


실제 환경에서는 JDK에서 java.io 패키지를 이용하여 FileReader, BufferedReader I/O 클래스에서 자주 사용된다.

LineNumberReader reader = new LineNumberReader(new BufferedReader(new FileReader("file-path")));
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("file-path")));

 

반응형

댓글