Link
Today
Total
12-18 07:23
Archives
관리 메뉴

초보개발자 긍.응.성

6. Decorator Pattern (데코레이터 패턴) 본문

설계패턴(Design Pattern)

6. Decorator Pattern (데코레이터 패턴)

긍.응.성 2019. 11. 28. 03:23
반응형

동적으로 추가할 수 있는 기능들을 덧붙여야 하는 상황에 사용하는 Decorator Pattern에 대해 알아보자.

 

 

문제점

 

이해를 위해 간단한 예시로 카페에서 음료를 주문 받는다고 하자. 음료중엔 아메리카노, 라떼 등을 포함해 많은 종류의 음료가 존재할 것이다. 이는 Beverage라는 추상클래스를 만들고 이를 상속하는 여러 종류의 음료를 생성하여 표현할 수 있다.

음료에는 휩추가, 시럽 추가등의 첨가물을 추가할 수 있다. 이러한 모든 경우에 대해 생겨날 수 있는 조합의 음료들을 정의한다면 중복되는 코드가 많아진다는 문제점이 있다.

HouseBlend음료인데 토핑에 따라 클래스가 생성되면 중복되는 코드가 많아지는 문제점이 생긴다

이러한 문제를 해결하기 위해 abstract class인 Beverage에 속성으로 첨가물의 추가여부에 대한 변수를 가지게 만들어 중복되는 클래스의 생성을 줄일 수 있다.

 

하지만 생성되는 클래스의 수는 줄었지만 Berverage 의 method인 cost()는 아래와 같이 implement 되며

 

새로운 첨가물 종류의 추가나 가격의 변동으로 인해 잦은 변경이 일어날 수 있다. 이는 Beverage 클래스를 상속하여 종속성이 있는 클래스들에게 매 변경마다 새로 컴파일 되어야하는 문제를 만든다. 또한 모카시럽을 두번 뿌리는 음료는 위의 코드로 제작할 수 없다. 이는 잘못된 설계이다.

 


Decorator Pattern?

 

이를 위해 등장한 것이 Decorator Pattern 이다. Decorator Pattern에 대한 기본적인 아이디어는 다음과 같다.

 

 The decorator adds its own behavior.
 You can use one or more decorators to wrap an object.
 We can pass around a decorated object in place of the original (wrapped) object.
 Decorators have the same super type as the objects they decorate.
 We can decorate objects dynamically at runtime with as many decorators as we want.

 

1. Decorator는 자기 자신을 가지며

2. 하나이상의 Decorator로 객체를 꾸밀 수 있다.

3. Decorator객체는 기존의 Original Object를 대신할 수 있으며(상속관계를 이용)

4. Decorator는 자신이 Decorate하는 Component의 super type과 동일한 super type을 가진다

5. Runtime에 동적으로 decorate를 여러번 할 수 있다

 

기본적인 Decorator Pattern의 Class Diagram은 다음과 같다.

Component는 Original Object의 추상 클래스이며, ConcreteComponent는 추상 클래스인 Component를 상속하여 구현된 Concrete Object이다.  특이한 점으로는 Decorator클래스도 Component를 상속함과 동시에 속성으로 가지고 있다는 점이다. 즉 Decorator가 자기자신을 가지고 있는다는 것이 상위 클래스인 Component를 속성으로 가진다는 것이다.

 


적용 예시

 

이러한 특징으로 인해 여러번 Decorate할 때 자신이 Decorate한 Component를 가지고 있으며 중복해서 Decorate가 가능한 것이다. 다시 위의 커피집 예시에  Decorator 패턴을 적용해 보자.

 

Beverage는 다이어그램에서 Component로 cost()계산을 abstract하게 남긴다. ConcreteComponent인 Espresso는 Component인 Beverage를 상속받으며 cost()를 implement한다.

 

이제 Decorator인 CondimentDecorator를 보자. 위에서 언급한 것 처럼 Beverage를 상속받으며 속성으로 Beverage를 하나 가지고 있는 것을 볼 수 있다. 그리고 Decorate할 Mocha는 CondimentDecorator를 상속받으며 생성자에서 파라미터로 Beverage를 받아 자신의 속성으로 갖고 있음을 알 수 있다.

 

 

실제 사용의 예시이다. new DarkRoast()를 통해 ConcreteComponent인 DarkRoast 인스턴스를 하나 생성한다. 이는 상위 클래스 Component인 Beverage로 가리킬 수 있다. 이후 등장하는 new Mocha()와 new Whip()은 Decorator들이며 위에서 Beverage형으로 할당한 객체를 넘겨 이를 다시 Beverage객체로 받는다. 이는 ConcreteComponent와 Decorator가 모두 같은 super type인 Component를 상속하고 있어서 가능한 것이다.

 

이는 Design Principle중 OCP를 잘 따른 패턴이다. 새로운 ConcreteComponent나 Decorator가 추가되더라도 기존의 코드에 수정은 일어나지 않으며 추가만 하면 되기 때문에 매우 확장성이 좋다.

 


Java I/O에서 사용되는 Decorator Pattern

 

 

기존에 Java I/O를 위해 사용되는 InputStream도 Decorator Pattern을 매우 잘 따르는 사례이다.

 

FileInputStream, StringBufferInputStream과 같은 Concrete한 InputStream이 존재하며 읽어오는 방식에 대해 여러 필터를 Decorator방식으로 적용하여 이용할 수 있도록 구현하였다.

 

처음 자바 파일입출력에 대해 배울 때 복잡하고 이상한 형태의 구조라 생각하였는데 이것은 다 Decorator Pattern을 따라 구현한 것이였다...

ex. InputStream in = new BufferedInputStream( new FileInputStream(“test.txt”));

 

 


정리

 

Decorator Pattern은 기능을 동적으로 유연하게 확장 할 수 있게 해주는 패턴이며 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우 각 추가 기능을 Decorator Class로 정의 한 후 필요한 Decorator 객체를 이용할 수 있도록 한다.

 

Decorator에서는 Component를 내부에서 가짐으로 Composition & Delegation 방식을 사용하며, ConcreteComponent와 같은 parent type을 갖는다는 것이 특징이다.

 

단점으로는 많은 하위 클래스들을 만들어 낼 수 있다는 것과 이 패턴에 익숙하지 않은 사람이 볼때 정말 이해하기 힘들다는 점이다.

 

반응형
Comments