[디자인패턴] Decorator Pattern, 데코레이터 패턴

배경(Why)

도서관 소식을 유저에게 알리는 프로그램이 있다.

초기 요구사항에는 전달방식이 이메일 하나만 있었다.

그리고 전달방식에 Facebook, Slack이 추가되었다.

subclass로 둬 추가 요건을 반영했다.

그러자 한 사용자가 여러 Notifier를 가질 수 있다는 요건이 추가되었다.

모든 조합을 subclass로 뒀다... 너무 복잡해지고 한 클래스에 많은 책임이 생겨 SRP를 위배한다! 어떻게 개선할 수 있을까?

How

추가요건을 상속으로 해결하려고 하다보니 이런 문제가 발생했다. 상속은 다음과 같은 문제가 있다.

  • static하다. 따라서, object의 행위를 runtime에 바꿀 수 없다. 만약 그렇게 하려면 아예 object를 다른 subclass로 바꿔야한다.
  • 한 행위에 대해 여러 클래스를 상속받을 수 없다.

이를 Composition 또는 Aggregation으로 해결하고자 한다!

  • Composition: Wrapper(Person)과 Wrapped(Heart)가 같은 생명주기를 갖는다.
// 전체 생성
Person person = new Person();
// 전체 생성자 내
Person() { heart = new Heart(); }
  • Aggregation: 각각 다른 생명주기를 갖는다.
Heart heart = new Heart();
Person person = new Person(heart);

이를 이용해 다음과 같이 구현하다고 생각해보자

  • Wrapper는 Wrapped와 같은 Interface를 구현한다.
  • Wrapper가 그 Interface를 멤버변수로 갖는다.

이렇게 구현한다면,

  • Wrapped class인 Notifier를 runtime에 갈아끼울 수 있게된다!
  • 클래스들이 하나씩 참조를 하게 되면(chain) 위임(delegate)을 통해 여러 클래스의 행위를 사용할 수 있다.

What

위와 같이 동작하는 Decorator Pattern이란,

주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴이다.


  • Component: Wrapper, Wrapped class가 구현할 Common Interface
  • Concrete Component: Wrap 될 기본기능
  • Base Decorator: Wrapped class를 참조한다. 그 Wrapped class의 타입을 Component로 둬 Concrete Component, Decorators를 참조할 수 있게된다. 또, 모든 들어오는 연산을 Wrapped Object에 위임한다.
  • Concrete Decorators: Component에 추가할 행위를 정의

언제 쓸까?(When)

  • extra 행위를 런타임에 구성해야 할 때
    비즈니스 로직을 계층화해 여러 조합의 로직을 런타임에 구성할 수 있다.
  • 상속으로 문제를 해결할 수 없는 상황
    final keyword 사용 등

Pros

  • SRP을 만족한다.
  • 객체의 책임을 런타임에 붙이고, 떼어낸다.
  • 여러 행위를 조합할 수 있다.

Con

한 번 구성하면 Wrapped를 없애기 쉽지 않다!

실제 사용케이스

Java I/O에서 사용된다.

serialized Java objects in a Gzipped file가 있고 빠른 속도로 읽어와야하는 상황이라고 가정하자.

  1. inputstream을 연다.
FileInputStream fis = new FileInputStream("/objects.gz");
  1. 빠르게 하기 위해 버퍼를 사용한다.
BufferedInputStream bis = new BufferedInputStream(fis);
  1. gzipped 파일을 사용하기 때문에, 이를 unzip한다.
GzipInputStream gis = new GzipInputStream(bis);
  1. unserialize 한다.
ObjectInputStream ois = new ObjectInputStream(gis);

이렇게 책임을 하나씩 붙이고 다음과 같이 사용한다!

SomeObject someObject = (SomeObject) ois.readObject();

상황에 맞게 자유롭게 stream을 데코레이트하는 것을 확인할 수 있다!

ref

https://refactoring.guru/design-patterns/decorator
https://www.google.com/url?sa=i&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDecorator_pattern&psig=AOvVaw1UpIvHdtHYw7QbovWyFivp&ust=1617199397826000&source=images&cd=vfe&ved=0CAkQjhxqFwoTCNCbp7GX2O8CFQAAAAAdAAAAABAP
https://stackoverflow.com/questions/6366385/use-cases-and-examples-of-gof-decorator-pattern-for-io

좋은 웹페이지 즐겨찾기