ios dev kangwook.

03. Decorator Pattern 본문

Design Pattern

03. Decorator Pattern

kangwook 2022. 4. 30. 13:19
주어진 주어진 상황 및 용도에 따라 어떤 객체에 책임과 기능을 객체의 결합을 통해 동적으로 유연하게 추가하는 패턴


Decorator Pattern

Decorator pattern은 객체에 동적으로 추가 책임을 부여한다. 뿐만 아니라 기능 확장을 위한 자식 클래스에 대해 유연한 대안을 제공한다.

  • Component : 동적으로 추가할 서비스를 가질 수 있는 객체
  • ConcreteComponent : Decorate를 받을 객체 → 기능 추가를 받을 실질적인 기본(base) 객체
  • Decorator : Decorate를 할 객체의 추상 클래스
  • ConcreteDecorator : Decorator를 상속받아 구현할 다양한 기능 객체들

  • Decorator Pattern 특징
    • ex) DarkRoast 커피

  • Decorator 패턴은 base가 되는 객체(Concrete Component)를 decorate하는 객체들이 겹겹이 감싸고 있는 모양
  • 위의 예를 참고하자면 cost()라는 함수를 호출하게 되면 감싸고 있는 각각의 객체들이 안쪽의 cost()를 호출하게 되고 결과적으로 base가 되는 객체인 DarkRoast부터 값을 반환하면서 모두 합한 가격을 출력할 수 있음

Decorator Pattern 예제

  • Coffee 전문점의 음료 메뉴와 가격 구성
    • base가 되는 객체를 구성하는 Component 클래스인 Beverage 클래스
    • Decorate를 받을 실질적인 객체인 HouseBlend, DarkRoast, Espresso, Decaf 클래스
    • Decorate할 추상 클래스인 CondimentDecorator 클래스
    • ConcreteDecorator인 Milk, Mocha, Soy, Whip 클래스

  • Component : Beverage class
    • 음료의 이름을 반환하는 getter 메서드와 가격을 반환하는 추상 메서드 cost()
public abstract class Beverage {
	String description = "Unknown Beverage";
	
	public String getDescription() {
		return description;
	}

	public abstract double cost();
}
  • Decorator : CondimentDecorator class
public abstract class CondimentDecorator extends Beverage {
	public abstract String getDescription();
}
  • ConcreteComponent : Espresso, HouseBlend class
    • 기본이 되는 객체인 음료(Beverage)를 상속받은 구현 클래스(Concrete Class)
public class Espresso extends Beverage {
	public Espresso() {
		description = "Espresso";
	}

	public double cost() {
		return 1.99;
	}
}

public class HouseBlend extends Beverage {
	public HouseBlend() {
		description = "House Blend Coffee";
	}

	public double cost() {
		return .89;
	}
}
  • ConcreteDecorator : Mocha, Soy, Whip class
    • 각각의 첨가물 종류에 따라 description과 가격을 추가해주는 메서드 구현
public class Mocha extends CondimentDecorator {
	Beverage beverage;
	
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}

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

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

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

	public String getDescription() {
		return beverage.getDescription() + ", Soy";
	}

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

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

	public String getDescription() {
		return beverage.getDescription() + ", Whip";
	}

	public double cost() {
		return beverage.cost() + .10;
	}
}
  • Main 함수에서 테스트
public class StarbuzzCoffee {
	public static void main(String [] args) {
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() + " $" + beverage.cost());
		
		Beverage beverage2 = new HouseBlend();
		beverage2 = new Soy(beverage2);      // 첫 번째 wrapping
		beverage2 = new Mocha(beverage2);    // 두 번째 wrapping
		beverage2 = new Whip(beverage2);     // 세 번째 wrapping
		System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
	}
}
Espresso $1.99
House Blend Coffee, Soy, Mocha, Whip $1.34

추가적인 내용

  • Java의 I/O 클래스
    • 자바의 I/O 클래스는 Decorator Pattern으로 설계 되어 있음
    • 주로 FileInputStream을 구현할 때 BufferedInputStream을 감싸서 구현함

결론

  • Decorator 패턴은 객체에 동적 기능을 추가하기 위해 구조를 개선하는 구조 패턴(Structural Pattern)
  • 장점
    • 객체에 동적으로 기능 추가가 간단하게 가능
    • 기본적인 데이터에 첨가할 데이터가 다양하고 일정하지 않을 때 효율적
  • 단점
    • 자잘한 데코레이터 클래스가 계속 추가되어 클래스가 많아질 수 있음
    • 겹겹이 애워싸고 있기 때문에 본래의 객체의 정체를 알기 힘들고 복잡해질 수 있음

본문은 Head First Design Pattern (2004) 를 참고하여 공부하고 작성하였음


 

  •  

'Design Pattern' 카테고리의 다른 글

06. Singleton Pattern  (0) 2022.08.21
05. Abstract Factory Pattern  (0) 2022.08.20
04. Factory Method Pattern  (0) 2022.08.18
02. Observer Pattern  (0) 2022.03.13
01. Strategy Pattern  (0) 2022.03.12
Comments