LikeTech Main.

Daily design pattern - 데코레이터 패턴 - decorator pattern

Jihoon Na
Jihoon Na

Introduction

안녕하세요. 오늘은 데코레이터 패턴에 대해서 알아보도록 하겠습니다. 전략 패턴의 정의는 다음과 같습니다.

데코레이터 패턴이란 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. ref) Wiki

음 .. 책임을 덧붙이는 패턴인데, 일단 서브클래싱을 대신하여 쓴다는 걸 보니 단순히 서브클래싱을 만드는건 아니겠군요~?

그럼 본격적인 이야기로 들어가보겠습니다.

Contents

오늘은 장난감 회사에 소프트웨어 직무로 취직한 나신입 사원의 예제로 시작하겠습니다.

나신입 사원 : 안녕하세요. 나신입입니다~
김경력 대리 : 네 반가워요. 혹시 이번에 새로 저희 회사에서 론칭한 꾸미기 인형 알고있죠~?
나신입 사원 : 넵 알고있습니다.
김경력 대리 : 그 꾸미기 인형을 실제 주문을 넣을때 필요한 구현체가 필요해서요.
나신입 사원 : 아 넵넵!
김경력 대리 : 그게 커스터 마이징 주문이 매운 많은 꾸미기 인형이라 구현이 조금 복잡할 수 있는데, 가능하겠어요~?
나신입 사원 : 넵 열심히 해보겠습니다!
김경력 대리 : 그럼 우선 이 소스에서 시작해요. 이건 최상단에 있는 doll 클래스에요. 여기서 부터 시작해서 현재 기획에 나와있는 인형들을 구현해주세요.
나신입 사원 : 넵!

자리에 돌아온 나신입은 소스 관리 프로그램에서 아래와 같은 doll 클래스를 받아봅니다.

public abstract class Doll{
public abstract double cost();
}

그리고 실제 구현해야하는 기획서를 봅니다.

2021 최고 장난감회사 인형 기획서

  1. 인형의 모자를 새깔별로 추가할 수 있다.
  2. 목걸리 종류도 선택해 추가할 수 있다.
  3. 신발도 추가할 수 있다.
  4. 손에 악세사리도 추가할 수 있다.

흠 .. 좋아 자바는 객체지향 언어잖아? 저 doll을 상속 받아서 구체적인 구현체들을 만들면 되는거지 뭐!

우선... 경우의 수를 보자.. 모자 종류가 4개... 목걸이 종류가 3개.. 신발 종류가 2개.. 악세사리가 5개..

그러면...

구체적인 클레스는 각각 없는 것들도 포함해야하니 한개씩 더해서 (4+1) _ (3+1) _ (2+1) * (5+1) = 360 가지네!!

역시 현업이라는건 정말 힘든거구나~

좋아 뭐 주말 밤을 이용해서 하면 되지!

우선 아무것도 없는 인형부터 만들어볼까~

가격은 500원이네~

public clasee DollWithoutHatWithoutNecklaceWithoutShoesWithoutAccessories extends Doll{
public double cost(){
return 500.0;
}
}

좋아 시작이 반이라는 말이 있잖아~? 반다했다! 이제 359개만 더 만들면 돼!

이번엔 모자가있는.. 걸 만들자 모자 가격이 10원이니까 ..

public clasee DollWithHatWithoutNecklaceWithoutShoesWithoutAccessories extends Doll{
public double cost(){
return 510.0;
}
}

이번엔 모자는 있고 목걸리은 없는데 신발은 있는걸로~

public clasee DollWithHatWithoutNecklaceWithShoesWithoutAccessories extends Doll{
public double cost(){
return 520.0;
}
}

(2일 밤 뒤)...

휴~ 다끝냈다 ~ 역시 현업이라는건 정말 너무 일이 많네~

나신입 사원은 마무리한 작업 내용을 소스 관리 프로그램에 올리고 김경력 대리를 찾아간다.

나신입 사원 : 안녕하세요. 나신입입니다~
김경력 대리 : 네 ~ 들어오세요~
나신입 사원 : 대리님 말씀해주신 작업 진행 완료하였습니다.
김경력 대리 : 네~ 한번 볼까요~?
김경력 대리 : 읭? 360개의 클래스..? 음.. 나신입씨?
나신입 사원 : 네!
김경력 대리 : 열심히 해온건 알겠는데.. 이렇게 진행하면 안돼요.
나신입 사원 : 오 무슨 말씀이시죠..?
김경력 대리 : 우선 클래스가 너무 많아져서 관리할게 많아지고..
김경력 대리 : 저희는 목걸이를 하나만, 액세서리를 하나만 할 수 있다고 규정한 적이 없어요.
김경력 대리 : 그러면 경우의 수가 엄청 많아지겠죠? 그리고 새로운 악세서리가 생기면 그떈 어떻게 할거에요..?
나신입 사원 : 음 그렇네요...
김경력 대리 : 우린 사실 너무 커스터 마이징이 다양하기 때문에 그 인형을 구성하는 것들이 무엇인지 하나하나 클래스로 가지고 있는 것은 의미 없어요.
김경력 대리 : 한번 OCP와 데코레이터 패턴에 대해서 공부해 보고 다시 작업해주세요.
나신입 사원 : 넵 알겠습니다!

자리로 돌아온 나신입 사원은 ocp가 무엇인지 검색한다.

Open closed priciple? 뭐지.. 약간.. 그 소리없는 아우성 같은건가..???ㅎ

OCP 즉 개방-패쇄 원칙은 소프트웨어 개체는 확장에 대해 열려 있어야하고, 수정에 대해서는 닫혀있어야한다. ref) Wiki

흠 확장에 대해서 열려있어야한다..? 지금 사례에서는 뭐.. 여러가지 인형의 구현체에 대해서 열려있어야한다.. 그런 뜻이겠지?

데코레이터 패턴이란 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. ref) Wiki

흠..... 일단 내가 한 것 처럼 서브클래싱을 하는게 아니라.. 어떤 객체에 책임을 덧붙이는 것이라.. 일단 인터넷에 있는 대로 한번 따라해봐야 겠다!

우선 아까 대리님이 주신 추상 클래스는 그대로 두고..

public abstract class Doll{

    public abstract double cost();

}

그리고 인터넷에 있는 예제처럼 각각 악세서리에 대해서 상속할 추상클래스를 만들자..

public abstract class DollDecorator extends Doll{

    public abstract double cost();

}

그리고 그 각각의 아이템들에 대해서 위의 DollDecorator을 상속 받도록 해서 구현해보자!


public class YellowShoes extends DollDecorator {
Doll doll;

    public YellowShoes(Doll doll){
        this.doll = doll;
    }

    public double cost(){
        return 30.0 + doll.cost();
    }

}

public class RedShoes extends DollDecorator {
Doll doll;

    public RedShoes(Doll doll){
        this.doll = doll;
    }

    public double cost(){
        return 15.0 + doll.cost();
    }

}

public class RedHat extends DollDecorator {
Doll doll;

    public RedHat(Doll doll){
        this.doll = doll;
    }

    public double cost(){
        return 20.0 + doll.cost();
    }

}

이런 식으로 각각의 아이템들을 만들고...

public class DollCompany{
public static void main(String args[]){
Doll doll = new Doll();
doll = new YellowShoes(doll);
doll = new RedHat(doll);

        System.out.println(doll.cost());
    }

}

아.. 사실 뭔가 악세사리를 추가하는 입장에서 그 돌이 이미 어떤 악세사리를 추가하고 있는지 굳이 알필요가 없기 때문에

테코레이트 즉 뭔가 하나 추가할 때 그것을 새로 알기 보다는 가장 최상위의 클래스를 상속하도록 해서 구현하는 거구나..

이렇게 하면 360개로 하나하나 클래스로 규정하지 않고도 확장을 할 수 있구나..

나신입 사원 : 안녕하세요. 나신입입니다~
김경력 대리 : 네 ~ 들어오세요~
나신입 사원 : 대리님 말씀해주신 작업 진행 완료하였습니다.
김경력 대리 : 네~ 한번 볼까요~?
김경력 대리 : 오~ 이번엔 잘 진행을 해왔네요~~ 수고 많으셨어요.
나신입 사원 : 오 감사합니다!
김경력 대리 : OCP와 데코레이터 패턴도 공부해 본거죠~?
나신입 사원 : 넵! 근데 한가지 궁금증이 있습니다! 사실 저렇게 하는 방법이 데코레이터 패턴이라고 검색해서 저렇게 하기는 했지만,
나신입 사원 : 그냥 doll의 내부 변수에 예컨대 shoes 변수를 가지고 있고 accessories 배열 변수를 가지고 있으면서 관리하면 되지 않나요?
김경력 대리 : 음 물론 그렇죠~ 그치만 뭔가 기존에 있던 것이 아닌것이 들어오게 되면 그땐 어떡할거에요? 예를 들어 지금은 화장하는 옵션이 없는데 화장 옵션이 들어온다면요.
나신입 사원 : 그럼 최상단의 doll class에 화장관련 변수를 넣으면 되지 않나요?
김경력 대리 : 근데 그건 좋지 않은 생각에요. 이미 이 클래스를 다른 어디서 마구 가져가서 사용할텐데, 이 클래스 자체를 변경한다는 것은 위험한 생각이에요.
나신입 사원 : 아.. 변경에는 닫혀있고.. 확장에는 열려있다는 것이 바로 그런 이야기군요..
김경력 대리 : 네 맞아요~~ 이해가 빠르시네요~ 얼른 들어가서 쉬세요..

나신입 사원은 OCP.. 그리고 데코레이터 패턴의 강점에 대해서 꺠달은 듯한 표정을 지으며 밝게 인사를 하고 퇴근을 하였다고 합니다~

이처럼 수많은 확장이 예상되는 프로그램에서는 확장에 유리한 패턴을 적용하여 사용해야합니다. 또한 이렇게 확장됨에 있어 기존의 코드가 변경이 되는 일은 없어야겠죠. 그렇기 때문에 OCP를 잘 지키는 데코레이터 패턴이 유용하게 쓰일 수 있었습니다 ~

오늘의 내용 요약 : 데코레이터 패턴은 기존의 코드는 수정하지 않고 확장에 용하도록 하는 패턴이다.

수고하셨습니다~~~~