데코레이터 패턴

핫다리·2023년 9월 7일
0

디자인패턴

목록 보기
3/3

데코레이터 패턴

객체에 추가요소를 동적으로 더할 수 있다. 훨씬 유연하게 기능을 확장할 수 있다.

예제로 학습해 봅시다.

문제

  1. 카페의 주문 시스템을 만든다.

  2. 고객은 음료를 주문할 때 우유, 두유, 모카, 휘핑크림등을 추가할 수 있다.

  3. 추후 옵션이 늘어날 수 있다.

  4. 각각을 추가할 때마다 커피 가격이 올라가는 점을 고려해야 한다.

구현

상속을 사용한 설계를 보자


Beverage클래스에 우유, 두유 등등 추가할 수 있는 것들에 대한 boolean변수를 만들고 getter/setter를 추가했다. 그리고 서브 클래스를 구현했다

하지만 문제가 아직 남아있다.
1.첨가물이 바뀔때마다 코드를 계속 수정해야 한다
2. 종류가 많아지면 새 메소드를 추가해야 하고 슈퍼클래스에 cost()도 수정해야 한다.
3. 새로운 음료가 추가될 수도 있고, 특정 첨가물이 들어가면 안되는 경우도 있을것이다.
4. 첨가물을 두번 추가하면??

여기서 디자인 원칙!

클래스는 확장에는 열려있지만, 변경에는 닫혀있어야 한다.(OCP)

이를 해결하기 위해 모순적으로 보이지만 코드를 확장하게 해주는 기법이 있습니다.

데코레이터 패턴

먼저 DarkRoast라는 객체가 있다

음료수 이름이다

여기에 모카를 추가해보자

여기에 휘핑크림을 추가해보자

이런식으로 객체에 추가요소를 동적으로 더할 수 있다.

모카와 휘핑크림이 데코레이터라고 할 수 있다.
이 데코레이터는 DarkRoast(객체)와 같은 형식이다.

whip의 cost()를 호출하면 mocha의 cost로 가서DarkRoast의 cost를 호출하고 다시 계산하면서 빠져나온다.

이를 UML로 표현하면 다음과 같다

데코레이터와 데코레이터로 감싸는 객체의 형식을 맞추기 위해 Beaverage를 상속 받아 사용한다

코드로 보기

상위 추상 클래스

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

    public String getDescription(){
        return description;
    }

    public abstract double cost();
}

꾸밈당할 객체들

public class DarkRoast extends Beverage{
    
    public DarkRoast(){
        this.description = "다크로스트";
    }

    @Override
    public double cost() {
        return .99;
    }
}

public class Espresso extends Beverage{
    public Espresso(){
        description = "에스프레소";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}

꾸밀 객체(데코레이터)들 상위 클래스

public abstract class CondimentDecorator extends Beverage{

    Beverage beverage; //데코레이터가 감쌀 객체를 지정

    public abstract String getDescription();

}

데코레이터들

public class Mocha extends CondimentDecorator{

    public Mocha(Beverage beverage){
        this.beverage = beverage;

    }

    @Override
    public double cost() {
        return beverage.cost() + .20;
    }

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


public class Soy extends CondimentDecorator{

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

    }

    @Override
    public double cost() {
        return beverage.cost() + .15;
    }

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

//필요한거 만들어서 계속추가

데코레이터 사용하기

public class StarbuzzCoffee {

    public static void main(String args[]){
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + "$" + beverage.cost());

        Beverage beverage2 = new DarkRoast();
        beverage2 = new Mocha(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);

        System.out.println(beverage2.getDescription() + "$" + beverage2.cost());

        Beverage beverage3 = new HouseBlend();
        beverage3 = new Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription() + "$" + beverage3.cost());


    }
}

데코레이터 패턴의 특징

  • 데코레이터 클래스의 형식은 그 클래스가 감싸는 클래스 형식을 반영한다.
  • 데코레이터는 자기가 감싸고 있는 구성 요소의 메소드를 호출한 결과에 새로운 기능을 더함으로 행동을 확장한다.
  • 구성 요소를 감싸는 데코레이터의 개수에는 제한이 없다.
  • 구성 요소의 클라이언트는 데코레이터의 존재를 알 수 없다.
  • 데코레이터 패턴을 상요하면 자잘한 객체가 매우 많이 추가될 수 있고, 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해 진다. -> 팩토리와 빌더 패턴으로 보완 가능
profile
일단 만들고 본다

0개의 댓글