본문 바로가기
공부/디자인 패턴

3. Decorator Pattern (Head First Design Patterns)

by 김주현3902 2024. 10. 20.

Decorator Pattern은 객체에 추가적인 책임을 동적으로 부여한다.

 

Design Principle

Classes should be open for extension, but closed for modification

객체 지향의 5가지 원칙 중 OCP 원칙이다.

확장에는 개방적이여야 하고, 변화에는 폐쇄적이어야 한다.

 

Beverage 객체가 있고, cost()라는 공통 메소드를 가진다. (비용을 반환한다.)

Beverage 객체를 상속하는 다양한 음료들(ex: espresso)의 여러가지 추가 옵션들을 설정하는 것이 목적이다.

우선 음료의 종류를 정한 후, 각각의 옵션(ex: milk)이 추가될 때마다 점점 이를 감싸는 형태가 된다.

모든 옵션이 결정된 후, cost 메소드를 실행하면 제일 바깥쪽부터 안쪽으로 순차적으로 실행된다.

 

beverage 클래스를 각 음료들과 condimentDecorator가 상속받는다.
각각의 옵션들이 condimentDecorator를 상속받는다.

bevarage는 description 변수를 가지고, getdescription 메소드와 cost라는 추상 메소드를 가진다.
condimentDecorator는 beverage를 상속하며. beverage 변수와 getDesciprtion라는 추상 메소드를 가진다.
각 음료들은 beverage를 상속하며 생성자에서 description 변수를 각각에 맞게 변형하며, cost 함수에서 각각에 맞는 가격을 반환한다.
각 옵션들은 condimentDecorator를 상속하며 생성자에서 beverage를 입력받는다.
또한 getDescription에서 자신이 가지고 있는 beverage의 getDescription에 자신의 이름을 추가해 반환한다.
cost에서도 beverage의 가격에 자신의 가격을 추가해 반환한다.

public abstract class Beverage {
    public String description = "unknown";

    public String getDescription() {
        return description;
    }
    
    public abstract double cost();
}
public class Espresso extends Beverage{

    public Espresso() {
        description = "Espresso";
    }
    
    @Override
    public double cost() {
        return 1.99;
    }
}
public class Decaf extends Beverage{

    public Decaf() {
        description = "Decaf";
    }

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

각 음료들은 Beverage를 상속받으며, 생성자에서 description을 자신에 맞게 변경하고 cost 메소드에서 자신의 가격을 반환한다.

public abstract class CondimentDecorator extends Beverage{
    public Beverage beverage;
    public abstract String getDescription();
}
public class Milk extends CondimentDecorator {

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

    @Override
    public String getDescription() {
        return "milk " + beverage.getDescription();
    }

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

}
public class Mocha extends CondimentDecorator {

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

    @Override
    public String getDescription() {
        return "Mocha " + beverage.getDescription();
    }

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

각 옵션들은 생성자에서 Beverage를 입력받아 저장한다.

getDescription 메소드 에서는 자신의 이름에 가지고 있는 beverage의 getDescription으로 반환된 정보를 합쳐서 반환한다.

cost 메소드도 getDescription과 같은 방식으로 동작한다.