데코레이터 패턴 (Decorator Pattern)

종명·2021년 4월 25일
0

헤드 퍼스트 디자인 패턴을 읽고 정리한 글입니다.

데코레이터 패턴(Decorator Pattern)에서는 객체에 추가적인 요건을 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

예제 코드

다음은 음료에 첨가물을 조합하는 예시이다.
먼저, Beverage 클래스를 만들어보자. 이 클래스는 추상 클래스이며, 두 가지 메소드를 가지고 있다.

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

    public String getDescription() {
        return description;
    }

    public abstract int cost();
}

첨가물을 나타내는 CondimentDecorator 클래스이다. Beverage 객체가 들어갈 자리에 들어갈 수 있도록 Beverage를 확장하였다.

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

실제 음료 클래스 두개를 생성하였다. Beverage를 확장하고, 생성자에서 Beverage로 부터 상속받은 description 값을 설정한다. 그리고 cost 메서드에 가격을 계산한다.

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

    @Override
    public int cost() {
        return 2000;
    }
}
public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "하우스 블렌드 피피";
    }

    @Override
    public int cost() {
        return 3000;
    }
}

마지막으로 Mocha, Whip, Soy를 생성하였다.

  • 이 클래스는 데코레이터이기에 CondimentDecorator를 확장한다.
  • Beverage에 대한 래퍼 인스턴스가 들어있다. 이 변수는 감싸고자하는 음료를 저장하기 위한 인스턴스이다.
  • 생성자를 통해 감싸고자하는 음료 객체를 전달받는다.
  • getDescription 메소드에 각 첨가물에 대한 설명을 추가한다.
  • cost 메소드에 첨가물을 추가한 가격을 계산한다.
class Mocha extends CondimentDecorator {
    Beverage beverage;

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

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

    @Override
    public int cost() {
        return 500 + beverage.cost();
    }
}
class Whip extends CondimentDecorator {
    Beverage beverage;

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

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 휘핑크림";
    }

    @Override
    public int cost() {
        return 300 + beverage.cost();
    }
}
public class Soy extends CondimentDecorator {
    Beverage beverage;

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

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

    @Override
    public int cost() {
        return 700 + beverage.cost();
    }
}

main 메소드에서 예제를 실행해보자.

Beverage beverage1 = new Espresso();
System.out.println(beverage1.getDescription() + " " + beverage1.cost() + "원");

beverage1 = new Soy(beverage1);
System.out.println(beverage1.getDescription() + " " + beverage1.cost() + "원");

Beverage beverage2 = new HouseBlend();
System.out.println(beverage2.getDescription() + " " + beverage2.cost() + "원");

beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " " + beverage2.cost() + "원");
에스프레소 2000원
에스프레소, 두유 2700원
하우스 블렌드 피피 3000원
하우스 블렌드 피피, 모카, 모카, 휘핑크림 4300원

데코레이터 패턴은 자바 I/O에서 사용하고 있다.

  • FileInputStream, StringBufferInputStream 등 InputSteam들은 데코레이터로 포장될 구성요소 역할을 한다. (Expresso, HouseBlend)
  • 그리고 FilterInputStream은 추상 데코레이터 (CondimentDecorator)이고 하위 4가지 서브클래스가 구상 데코레이터들(Mocha, Whip ...)이 있다.

0개의 댓글