데코레이터 패턴

반응형
반응형

 

데이코레이터는 장식입니다. 

 지금 보고 있는 사진은 거북이 장식입니다. 예쁘죠? 물론 프로그래밍 상에서의 데코레이터는 살짝 다른 느낌이 들긴 하지만요. 아무튼 이름 만 봐서는 데코레이터는 무언가를 꾸미는 패턴이라고 생각하게 될것 같습니다. 막상 코드를 보면 이게 왜? 데코레이터지?라는 생각이 들지도 모릅니다. 보통 장식이라는게 정해진 위치에 놓잖아요.

커튼에 달린 장식품을 냉장고에 올려놓으면 이상한거처럼 이 패턴도 막 사용하면 안됩니다. 물론, 커튼 장식품을 냉장고에 가져다 놔도 아무상관은 없습니다. 아무리 멋있는 장식품이어도 아무데나 놓으면 그저 잡동사니에 불과하니까요.

이 처럼 데코레이터 패턴은 아무렇게나 사용이 가능은 하지만, 쓸데 없이 아무렇게나 사용하면 아무 쓸데 없는 패턴일 겁니다.

 

 데코레이터 패턴은 객체 지향의 특징 중 하나를 잘 나타내고 있습니다. 

바로 OCP 이 특징은 다른 객체 지향 패턴에서도 많이 등장하는 패턴입니다. 예를들어 옵저버 패턴이라던지, 브릿지 패턴 턴 등에서 나타나고 있죠, 하지만 이 패턴의 다른 패턴과의 차이점은 결합도 입니다. 다른 패턴같은 경우, 결합도를 어떻게든 낮추려고 노력하지만,  이 패턴을 사용하게 되면 결합도가 올라갑니다. 물론, 이 패턴에 다른 패턴을 사용해서 결합도를 낮추는 방법이 있겠지만, 단독으로 사용할때는 오히려 결합도가 높아진다는 느낌을 받았습니다.( 물론, 개인적인 의견입니다. )하지만, 그 방법이 강력해서 자바IO에서도 데코레이터 패턴을 채용해서 사용하고 있습니다. 

 

 

이제 본격적으로 코드를 보면서 설명하겠습니다. 요즘들어 예제 만드는게 너무 귀찮아서 그냥 해드 퍼스트에 있는 내용으로 대체 해야할 것 같네요..ㅜㅜ;; 

 

 해드 퍼스트에서는 카페를 예로 들고 있습니다. 카페에 가면 커피가 있죠. 제가 커피를 마시지 않기 때문에 잘 모르지만, 아무튼 다양한 커피가 있다고 합니다. 또, 커피를 주문하게 되면 다양한 옵션들을 추가할 수 있다고 합니다. 저는 추가해본적이 없기 때문에 잘 모르겠습니다. 책이 미쿡꺼라 한국에서는 다를 수도 있겠네요. 

 

커피는 넓은 의미에서 음료입니다.

public abstract class Beverage {
    protected String description;

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

근데 왜 abstract일까요? 정확한 이유는 코드를 더 봐야 알겠지만, 대충 수정해서는 abstract를 사용하는것이 좋을 것 같네요. 수정할 수 는 있는데, 수정하면 수정할게 너무 많거든요.

네... cost는 상속받은 클래스에서 구현하게 만들어놨네요. 이제 본격적으로 커피를 내려봅시다.

public class DarkRoast extends Beverage {
    public DarkRoast() {
        description = "다크 로스트";
    }

    @Override
    public double cost() {
        return .99;
    }
}

네! 다크 로스트라는 커피입니다. 그리고 가격은 99센트라고 하네여. 미쿡돈이라 시세를 잘 모릅니다.ㅜㅜ 아무튼 그렇다고 합니다. 카페가면 여기에 추가해서 먹을 수 도 있다고 합니다. 그것을 구현해봅시다. (그게 1shot추가해주세요. 할때 그건가 봅니다. ㅎㅎ)

 

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

이것또한 interface가 마렵지만, 수정하게 되면 interface에서는 class를 상속받는 방법이 없기 때문에 어쩔 수 없이 abstract로 만들어 줍니다. 보니까 내용물을 추가할 수 있군요. 

 

public class Mocha extends CondimentDecorator{
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 모카";
    }

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

저는 모카를 선택했습니다. 이제 왜 desciption을 오버라드하려고 했는지 이해가 되는군요. 근데 cost는 왜 하지 않았을까요? 혹시... 람다로 만들려고 하는 큰 그림... 이 책이 자바5때 나온 책이긴 한데...... 대단하군요.

장난은 그만하고, 

 

Moca는 CondimentDecorator이자 Beverage입니다. 물론, 현실에서는 말도 안되죠, 모카가 음료인게 말이 됩니까? 

여기에서는 모카를 추가할때 기존에 존재하는 음료에 설명이랑 값을 추가해 넣을 수 있습니다.

예를 들어, 다크 로스트를 주문한다고 가정했다고 생각해봅시다.

그러면 이름 : 다크 로스트 , 가격 : 99센트

여기다 모카를 추가합니다. 그러면 이름 : 다크 로스트, 모카 / 99+22 센트 이런식으로 흘러갑니다.

 

이 패턴의 정수는 메인문입니다.

Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription() + " $ " + beverage2.cost());

모카도 음료이기 때문에 이 와같은 구성이 가능해집니다. 근데 굳이 decorator를 사용하지 않고, 그냥 beverage에 때려 박으면 되지 않을까 생각이 듭니다. 물론 그렇게 구현할 수 는 있습니다. 하지만, 그렇게 구현하면 DarkRoast객체가 쓸데 없이 복잡해지게 됩니다. 생각해보세요. 한개는 무난하게 추가할 수 있습니다. 모카를 2개, 3개 추가한다고 한다면, 모카를 추가하지 않는다면... 어마어마 많은 클래스를 만들거나 객체가 복잡해지는것을 막을 수는 없을 것 같습니다. 

물론, 방법이 아애 없는 것은 아닙니다. 아직 학습하지는 않았지만, 빌더 패턴이라는 걸 활용하면 어쩌면 가능할지도 모르겠네요. 하지만 학습하지 않았으므로 확답을 드리지는 못하겠습니다. 

 

이 패턴과 비교 되는 패턴은 총 2가지입니다.

하나는 위에서 말한 빌더 패터이고,

다른 하나는 브릿지 패턴입니다. 브릿지 패턴이 왜 유사한가하면 생긴건 비슷하지 않지만, 브릿지도 기능을 추가하는 역할을 하기 때문입니다.(물론, 범위가 달라 부딪힐 이유는 없지만요... )

 

 

반응형

'디자인패턴' 카테고리의 다른 글

싱글톤 패턴  (0) 2020.05.12
팩토리 패턴  (0) 2020.05.06
컴포지트 패턴  (0) 2020.05.01
옵저버 패턴  (0) 2020.04.28
퍼사드 패턴  (0) 2020.04.23

댓글

Designed by JB FACTORY