아무거나

[Design Pattern] Decorator Pattern 본문

Java & Kotlin/Java

[Design Pattern] Decorator Pattern

전봉근 2019. 12. 28. 21:04
반응형

데코레이터 패턴(Decorator Pattern)

동적으로 책임 추가가 필요할 때 사용한다.

 

 

Component: 실질적인 인스턴스를 컨트롤하는 역할 즉, decorator와 Component를을 컨트롤 하는 역할이다. (method 부분이 책임 부분) ConcreteComponent: Component의 실질적인 인스턴스의 부분이며, 책임의 주체의 역할 즉, Component가 컨트롤하는 객체 Decorator: Component와 concrete decorator를 동일시 하도록 해주는 역할 즉, Decorator은 Component가 될 수 있고, Component들을 가지고 있는 모양세를 가지고 있다고 생각하면 된다. ConcreteDecorator: 실질적인 장식 인스턴스 및 정의이며 추가된 책임의 주체 부분이다. (addedMethod는 Concrete Decorator에서 사용할 Method를 추가하는 부분)

데코레이터 패턴 예시 - 1

[요구사항]

  • 커피제조

    • 에스프레스: 커피의 기본
    • 아메리카노: 에스프레소 + 물
    • 카페라떼: 에스프레스 + 스팀밀크
    • 헤이즐넛: 아메리카노 + 헤이즐넛 시럽
    • 카페모카: 카페라떼 + 초콜릿
    • 캬라멜 마끼야또: 카페라떼 + 캬라멜 시럽
  • UML

    • IBeverage: 커피의 가격을 산출하는 인터페이스
    • Base: 실질적으로 책임져야할 부분 즉, 가격이 축적이됨을 Base에서 계속 가지고 있음
    • AbstAdding: 추가 재료들을 구현
    • Espresso: 재료

IBeverage 인터페이스를 생성하고 총 가격에 대한 책임을 묻는 getTotalPrice 메서드를 생성하자.
[IBeverage.java]

package com.bkjeon.decorator;

/**
 * <li>에스프레스: 커피의 기본</li>
 * <li>아메리카노: 에스프레소 + 물</li>
 * <li>카페라떼: 에스프레스 + 스팀밀크</li>
 * <li>헤이즐넛: 아메리카노 + 헤이즐넛 시럽</li>
 * <li>카페모카: 카페라떼 + 초콜릿</li>
 * <li>캬라멜 마끼야또: 카페라떼 + 캬라멜 시럽</li>
 *
 * @author bongkeun jeon
 */
public interface IBeverage {

    /**
     * 총 가격
     */
    int getTotalPrice();

}

Component의 실질적인 인스턴스의 부분이며, 책임의 주체인 Base 클래스를 구현하자.
[Base.java]

package com.bkjeon.decorator;

/**
 * <h2><b>Role: </b>ConcreteComponent</h2>
 * <p>
 *     Component의 실질적인 인스턴스의 부분이며, 책임의 주체이다.
 * </p>
 * @author bongkeun jeon
 */
public class Base implements IBeverage {

    @Override
    public int getTotalPrice() {
        return 0;
    }

}

Decorator를 구현하자. [AbstAdding.java]

package com.bkjeon.decorator;

/**
 * <h2><b>Role: </b>Decorator</h2>
 * <p>
 *     컴포넌트와 장식을 동일시 해주는 역할을 한다.
 * </p>
 * @author bongkeun jeon
 */
abstract public class AbstAdding implements IBeverage {

    private IBeverage base;

    public AbstAdding(IBeverage base) {
        super();
        this.base = base;
    }

    @Override
    public int getTotalPrice() {
        return base.getTotalPrice();
    }

    protected IBeverage getBase() {
        return base;
    }
}

ConcreteDecorator를 만들자. (Espresso.java, Milk.java)
[Milk.java]

package com.bkjeon.decorator;

/**
 * <h2><b>Role: </b>ConcreteDecorator</h2>
 * <p>
 *     실질적인 장식 인스턴스 및 정의이며, 책임이 추가되는 부분이다.
 * </p>
 * @author bongkeun jeon
 */
public class Milk extends AbstAdding {

    public Milk(IBeverage meterial) {
        super(meterial);
    }

    @Override
    public int getTotalPrice() {
        return super.getTotalPrice() + 50;
    }
}

[Espresso.java]

package com.bkjeon.decorator;

/**
 * <h2><b>Role: </b>ConcreteDecorator</h2>
 * <p>
 *     실질적인 장식 인스턴스 및 정의이며, 책임이 추가되는 부분이다.
 * </p>
 * @author bongkeun jeon
 */
public class Espresso extends AbstAdding {

    static protected int espressoCount = 0;

    public Espresso(IBeverage base) {
        super(base);
    }

    @Override
    public int getTotalPrice() {
        return super.getTotalPrice() + getAddPrice();
    }

    /**
     * 에스프레소 추가가격
     * 에스프레소 원샷: +100, 에스프레소 투샷: +70 (할인)
     * @return
     */
    private static int getAddPrice() {
        espressoCount += 1;
        int addPrice = 100;

        if (espressoCount > 1) {
            addPrice = 70;
        }
        return addPrice;
    }
}

메인 클래스를 작성하자.
[Main.java]

package com.bkjeon.decorator;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // base
        IBeverage beverage = new Base();
        boolean done = false;
        while (!done) {
            System.out.println("음료 현재 가격: " + beverage.getTotalPrice());
            System.out.println("선택: 1:샷 추가 / 2:우유 추가");

            switch (sc.nextInt()) {
                case 0: done = true;
                    break;
                case 1:
                    beverage = new Espresso(beverage);
                    break;
                case 2:
                    beverage = new Milk(beverage);
                    break;
            }
        }

        System.out.println("음료 가격: " + beverage.getTotalPrice());
        sc.close();
    }

}

실행결과

음료 현재 가격: 0
선택: 1:샷 추가 / 2:우유 추가
1
음료 현재 가격: 100
선택: 1:샷 추가 / 2:우유 추가
1
음료 현재 가격: 140
선택: 1:샷 추가 / 2:우유 추가
2
음료 현재 가격: 190
선택: 1:샷 추가 / 2:우유 추가
2
음료 현재 가격: 240
선택: 1:샷 추가 / 2:우유 추가
0
음료 가격: 240

 

참조: https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard

반응형
Comments