본문으로 바로가기

3. Decorator Pattern

category Design Pattern/Head FIrst Design Pattern 2019. 4. 21. 17:57

3. Decorator Pattern (장식 패턴)


3.1 Decorator Pattern 이란?

상속을 통한 기능확장이 아니라 구성을 통한 기능확장을 가능하게 해주는 패턴


- Decorator super class의 sub class(decorator)를 만들어 객체를 감쌈으로써 기능을 유연하게 확장할 수 있다.

  객체는 언제든지 감쌀 수 있기 때문에 실행중에 필요한 decorator를 마음대로 적용할 수 있다.

 (객체에 추가적인 사항을 동적으로 추가할 수 있게 해준다)

 (=원하는 기능을 가진 decorator를 미리 만들어 두기만 한다면 실행중 언제든 해당기능을 가진 decorator를 적용해 기능을 추가할 수 있다.)

- decorator들의 super class와 감쌀 객체의 super class는 같다. (같은 형식으로 맞춘다)

- 자신이 감쌀 객체를 레퍼런스로 가진다.

- 한 객체를 여러 개의 decorator로 감쌀 수 있다. 즉, decorator로 decorator객체를 감싸도 된다. (같은 형식이기 때문에)

- decorator는 자신이 장식하는 객체에 어떤 행동을 위임하는 것 외에도 원하는 추가적인 기능을 추가할 수 있다.

  즉, 추가되는 기능은 상속을 통해 생기는 것이 아니라 decorator객체와 구성객체간의 구성을 통해 추가된다.


*tip

행동을 물려받기 위한 목적의 상속이 아니라 상위 형식으로 맞추기 위한 상속은 괜찮다.


3.1.1 decorator 객체가 자신이 감쌀 객체와 같은 interface를 가져야하는 이유

자신이 감쌀 객체를 reference(instance variable)로 가지는 decorator객체는 해당 객체 뿐만 아니라

Decorator super class의 모든 sub class 즉, decorator객체들 또한 감쌀 수 있어야 하기 때문이다.


3.1.2 객체 구성

- 인스턴스 변수로 다른 객체를 저장하는 방식

- 상속을 사용한다면 행동이 컴파일시에 정적으로 결정되어 버리고 만다. 즉, super class에서 받은 것, 또는 코드를 통해서 override한 것만 쓸 수 있다.

  하지만 객체 구성을 활용하면 실행중에 decorator를 마음대로 조합해서 사용할 수 있다.

- 상속에만 의존한다면 새로운 행동을 추가해야 할 때마다 기존 코드를 바꿔야 하지만, 객체 구성을 사용한다면 언제든지 decorator를 새로 구현해서 새로운 행동을 추가할 수 있다. 구체적으로는 자신이 감싸고 있는 구성요소의 메소드를 호출한 결과에 새로운 기능을 더함으로써 행동을 확장한다.


3.1.2 Decorator Pattern의 단점

특정 형식에 의존하는 클라이언트(사용자)코드를 가져온 경우 decorator pattern 적용이 불가능하다.

구성 요소와 데코레이터는 interface로 형식을 맞추어 디자인하기 때문에 감싼 객체의 구체적인 형식을 알 수 없기 때문이다.


3.1.2 Decorator Pattern을 사용한 경우

- java.io 패키지



3.2 Coffee Order APP

- 다양한 종류의 커피가 있다.

- 우유, 휘핑등 재료를 추가할 수 있다.


3.2.1 상속을 통한 설계


3.2.1.1 음료 인터페이스

3.2.1.2 음료 interface 구현 클래스들


3.2.2 상속을 통한 설계한 Coffee Order App의 문제점

- 일부 Sub class에서는 적합하지 않은 기능을 Sub class에 추가하게 되는 문제 (상속을 통해 상속받아선 안되는 기능까지 상속받는 문제)

  홍차에는 휘핑 추가가 불가능한데 상속을 통해 디자인 했기 때문에 이러한 불가능한 것까지 가능하게 되어버린다.

  (확장할 때 상속을 통해 확장하면 그 기능은 컴파일시에 확정되고, 모든 sub class에서 똑같은 행동을 상속받아야 한다)

- 첨가물(구성요소)의 가격이 달라질때 마다 super class의 cost() 메소드를 수정

- 첨가물(구성요소)의 종류가 많아지면 새로운 setter getter 메소드를 super class에 추가해야하고 cost() 메소드도 수정해야한다. (유연한 확장성 보장x)


3.2.3 유연한 확장을 위한 코드란?

super class를 건드리지 않고 확장하는 것이 중요하다.

기존 코드는 건드리지 않고 새로운 코드를 만들어서 새로운 기능을 추가할 수 있어야 한다.

(closed) (open)


3.2.4 디자인 원칙 - OCP(Open-Closed Principle)

- 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다.

- 모든 부분에 OCP를 적용해서 디자인하기는 불가능 - 추상화의 복잡성, 여유의 부재

   => 가장 바뀔 확률이 높은 부분을 중점적으로 OCP적용


3.2.5 Decorator Pattern을 적용한 Coffee Order App


3.2.5.1 Component interface

감쌀 객체들의 타입임과 동시에 Decorator 객체들의 공통 타입이다.


3.2.5.2 ConcreteComponent 클래스들

Component interface를 구현하는 객체들


3.2.5.3 Decorator interface

decorator class들이 구현할 상위 interface.

이 interface는 Conponent interface를 구현함으로서 감싸질 객체와 Decorator 객체들의 형식을 같게 한다.


3.2.5.4 ConcreteDecorator 클래스들

Decorator interface를 구현하는 Decorator 클래스들이다.


3.2.5.5 Test class







'Design Pattern > Head FIrst Design Pattern' 카테고리의 다른 글

7. Adaptor Pattern  (0) 2019.05.23
6. Command Pattern  (0) 2019.05.20
4. Factory Pattern - (1) SImple Factory  (0) 2019.04.21
2. Observer Pattern  (0) 2019.04.21
1. Strategy Pattern  (0) 2019.04.15