Decorator Pattern의 SOLID의 OCP(Open Closed Principle)을 원칙으로 하는 Pattern입니다. Decorator Pattern은 예시로 바로 살펴보도록 하겠습니다.
아래 UML을 보시면 음료를 상속받아 각 커피를 구현하고 있는 형태입니다. 이 구조를 Decorator Pattern이 접목되며 변화하는 과정을 살펴보려합니다. Beverage 추상클래스를 상속받아 각 커피 클래스 만들었습니다. 이제 판매만 하면 될까요?
점점 고객들의 요구가 많아지고 있습니다. 우유를 추가해달라는 고객, 휘핑크림을 넣어달라는 고객 등 여러 요구사항에 맞춰주고 가격을 따로 받으려 합니다. 그럼 어떻게 해야하나요? 가장 단순한 방법은 아래와 같은 구조가 되는 것입니다. 아래 UML은 휘핑크림 추가에 대한 클래스들만 추가되었지만, WithMilk WithMilkAndWhip 등등의 클래스도 추가가 되어야 합니다.
이런 방식으로 점점 추가되는 구조는 끔찍할 것 같습니다. 커피에 추가되는 첨가료를 더하거나 뺄때마다 경우의 수가 너무 많아집니다. 모든 커피를 Beverage 클래스에서 상속받아 구현하는 것 말고 다른 방법을 생각해봐야겠습니다.
아래는 다음 접근방법 입니다. Beverage 클래스에서 milk, soy, mocha, whip 데이터를 가지고있으며 has, setter 메소드를 지원합니다. cost() 메소드 내부에서 각 첨가물의 has 여부에 따라 cost가 누적될 것 입니다.
코드로 구현하면 아래와 같은 형태가 되겠네요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package example.designpattern; /** * Created by linuxias on 19. 1. 7. */ public class Beverage { protected String description; private boolean milk; private boolean soy; private boolean mocha; private boolean whip; public float cost() { float condimentCost = 0.0; if (hasMilk()) condimentCost += milkCost; if (hasSoy()) condimentCost += soyCost; if (hasMocha()) condimentCost += mochaCost; if (hasWhip()) condimentCost += whipCost; return condimentCost; } ... } | cs |
위 코드의 문제점은 무엇일까요?
1. 가격변화가 있을 때 Beverage class가 변경됩니다.
2. 새로운 첨가물이 추가됬을 경우에도 Beverage class가 수정됩니다.
3. 새로운 beverage가 milk, soy등이 필요없을 때 상속받는 건 문제가 있어보입니다.
4. 고객이 mocha를 두번 원할 때는 어떻게 해야할까요? 지금구조로는 불가능할 것 같습니다.
Beverage Class는 많은 음료 클래스들이 상속받고 있는 슈퍼클래스로서 수정을 최소한으로 해야합니다. 만약 수정될 시 영향을 받는 클래스들이 매우 많아 전체 클래스를 다시 테스트해야 되는 불상사까지 발생할 수 있습니다.
Decorator Pattern 설명을 위해 도입부에 OCP에 대해 언급하였는데요, OCP를 다시 한번 정리하고 넘어가면 좋을 것 같습니다.
Open Cloased Principle
Classes should be open for extension, buf closed for modification.
Allow classes to be easily extended to incorporate new behavior without modifying existing code.
Caution : Don't try applying the Open-Cloased Principle to every signle case. Keep simple designs if possible!
위 원칙을 상기시키고 다시 구조를 생각해봅시다. Whip, Milk 등의 첨가물이 추가나 제거되어도 구조에 영향을 주지 않도록 하는 방법이 무엇이 있을까요?
Decorator Pattern의 UML은 아래와 같습니다. Decorator 추상 클래스가 Component를 상속받고 있으면서도, Association관계를 가지고 있습니다. 만약 상속구조가 없으면 어떻게 될까요? 한번 생각해보시기 바랍니다. Composition 과 Delegation을 이용한 구조란 것만 이해하시면 좋을 것 같습니다.
Decorator Pattern과 유사한 패턴으로는 Adapter Pattern과 Proxy Pattern이 있습니다.
Adaptor Pattern은 다른 인터페이스를 제공하는 패턴으로 클라이언트가 요구하는 타겟 인터페이스를 맞춰주고 서비스해주는 패턴입니다. Proxy 패턴은 Subject와 동일한 인터페이스를 제공합니다. Decorator 패턴은 향상된 인터페이스를 제공하게됩니다.
Decorator의 디자인 원칙은 OCP였습니다. Decorator의 중요한 메커니즘은 Composition과 Delegation이였던 것 기억나시나요? Decorator패턴은 추가적인 책임을 동적으로 객체에 붙여줄 수 있으며 유연하고 확장적인 구조를 가질 수 있다는 장점이 있습니다. 그에 반해 단점은 Decorator 자체가 객체가 되기 때문에 무수히 많은 객체가 생성될 수 있습니다.
'Software Architecture > Design Pattern' 카테고리의 다른 글
[Design Pattern] Adapter Patter (0) | 2019.01.08 |
---|---|
[Design Pattern] Mediator Pattern (0) | 2019.01.07 |
[Design Pattern] Iterator Pattern (0) | 2018.12.18 |
[Design Pattern] Template Method Pattern (0) | 2018.12.15 |
[Design Pattern] Null Object Pattern (0) | 2017.02.20 |