- 8가지 디자인 패턴 정리
"웹프로그래밍 스터디"라는 주제로 글을 작성하고자 한다. 그동안 공부하고 싶었던 주제를 정리했는데, 웹 개발의 전반적인 내용이 포함될 예정이다. 아마 바쁘다는 핑계로 글 올라오는 속도가 매우 느릴 수 있다. 하루에 한 시간씩이라도 시간을 내서 스터디해야 한다!!라고 나 자신에게 다짐해본다. 아래와 같은 목차로 진행 예정이며, 목차는 변경될 수는 있다.
https://brunch.co.kr/@springboot/15
"웹프로그래밍 스터디"과정 중, 디자인 패턴을 공부해야 하는 이유는 아래와 같다.
올바른 "객체지향 프로그래밍"을 하기 위해서
"스프링 프레임워크"를 깊게 이해하기 위해서
"테스트 주도 개발"을 객체지향스럽게 하기 위해서
"리팩터링"을 제대로 하기 위해서
이런 이유가 아니더라도, 소프트웨어 공학을 공부하는 사람으로서 당연히 디자인 패턴을 제대로 알고, 실무에 적용할 수 있어야 한다. 오래전에 공부하고 그동안 잊고 있었던 "디자인 패턴"을 이번 기회에 정리를 하고 싶어서 글을 작성하게 되었다.
해당 글은 예시가 억지스럽게 포함되어 있습니다. 그래서, 추후에 내용이 변경될 수는 있습니다. 내용은 가볍게 참고만 부탁드리며, 좋은 의견 있으시면 언제든지 말씀 부탁드립니다. 시중에 좋은 책들이 많으니, 좋은 책을 통해서 공부하시면 좋을 듯합니다. 이 글은.. 제 공부 겸 쓰는 글이에요.
디자인 패턴 중에 아래의 패턴을 공부한다. 패턴 선택 기준은 "스프링 프레임워크"에서 많이 사용하는 패턴 중 선택을 하였다. 나중에 다른 패턴 들도 공부할 예정이다.
싱글턴 패턴
전략 패턴
데코레이터 패턴
프록시 패턴
템플릿 메서드 패턴
템플릿 콜백 패턴
팩토리 메서드 패턴
어댑터 패턴
참고로 디자인 패턴을 공부하는 학생이라면, 이 세 권의 책은 꼭 보길 바란다.
[Gof 디자인 패턴-에릭감마 외3인-프로텍미디어]
[클린소프트웨어-로버트c마틴-제이펍]
[Head First Design Pattersn-에릭 프리먼-한빛미디어]
참고로 나는 [Gof의 디자인 패턴]은 조금 어려워서 아직 못 보고 있지만, 나중에 꼭 도전할 예정이다.
그리고, 이 글을 읽기 전에 객체지향 5가지 원칙에 대해서 전혀 모르고 있다면 선행학습으로 공부하길 바란다. 아래 링크는 본인이 작성한 글인데, 솔직히 추천하고 싶지는 않다. [클린 소프트웨어]라는 책을 추천한다. 혹시라도 아주 빠르게 이해하고 싶다면 아래 링크도... 뭐 봐줄만할 것이다.(내 글이지만 추천 못하겠음...)
https://brunch.co.kr/@springboot/30
"싱글턴 패턴"이란 하나의 인스턴스를 만들어서 사용하는 패턴이다. "싱글턴 패턴"을 이해하기 전에 우리는 Java JVM 스택(Stack) 영역과 힙(Heap) 영역, 그리고 HashCode대해서 알아야 하는데, 자세한 설명은 생략하겠다. 자바 프로그래밍 대부분의 책에서 설명이 되어있는데, [이것이 자바다 - 신용권님 - 한빛미디어]에서 140page를 읽어보기를 바란다. Java 의 HashCode는 힙 영역의 메모리 주소로 생성이 된다. 같은 인스턴스라면 같은 값이 되고, 다른 인스턴스라면 당연히 다른 값으로 설정된다. 즉, 싱글턴 패턴으로 생성된 인스턴스는 같은 해쉬 코드(HashCode)를 갖고 있다. 샘플 코드는 너무 간단하기 때문에 자세한 설명은 생략한다.
실행하면 싱글톤인 경우에는 같은 HashCode 값을, 아닌 경우에는 다른 HashCode 값으로 설정됨을 확인할 수 있다. 위와 같이 출력을 하지 않아도, Intellij(인텔리 J)에서는 디버깅 모드에서 인스턴스를 아래와 같이 확인할 수 있는데, 우측에 "Memory"를 보면 None싱글톤은 Count 가 2, 싱글톤은 1 인 것을 확인할 수 있다.
기억해야 할 싱글턴 패턴의 특징은 아래와 같다.
private 생성자를 갖는다.
하나의 인스턴스를 반환하는 getInstance() 메서드를 사용한다.
"싱글턴 패턴"은 클래스의 인스턴스를 하나만 만들어 사용하는 패턴
https://github.com/sieunkr/Design-Patterns/tree/master/DP_singleton
"전략 패턴"에 대해서는 어떻게 설명을 해야 할지 잘 모르겠다. 참고 서적의 설명을 인용하겠다. [Head First Design Patterns]에서는 아래와 같이 정의하였다.
"전략 패턴(Strategy Pattern)에서는 알고리즘 군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있다. 전략을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다."
[스프링 입문을 위한 자바 객체지향의 원리와 이해 - 김종민님 - 위키북스]에서는 아래와 같이 정의하였다.
"클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴"
책에서의 설명이 충분하지 않아서 위키북스의 링크도 참고하였다.
https://en.wikipedia.org/wiki/Strategy_pattern
설명을 위해서 내 마음대로 샘플 소스를 구현하였다. (설명의 이해를 돕기 위한 소스라서, 매우 억지스러울 수는 있다.) 샘플 예제는, 개발자의 도구에 대한 소스이다. 개발자의 두 가지 전략이 있다고 가정하겠다. 첫 번째 전략은 개발 툴 사용에 대한 전략이고, 두 번째 전략은 개발 방법론에 대한 전략이다. 첫 번째 전략에 대한 예로, 웹프로그래머인 나는 인텔리 J 를 주로 사용한다. 하지만, 아이폰 개발자는 애플 개발을 위해서 Xcode를 사용한다. (샘플 소스가 매우 억지스럽지만 이해 부탁한다.) 이때 웹프로그래머, 아이폰 개발자는 "컨텍스트"이고, 인텔리 J, XCode 는 "전략"이다. 컨텍스트 즉 웹프로그래머, 아이폰 개발자를 생성하고, 인텔리J, XCode 같은 전략을 컨텐스트에 주입하는 것이다. 두 번째 전략 역시, 개발자라는 컨텍스트에, 개발방법론이라는 전략을 주입하는 예시이다.
https://github.com/sieunkr/Design-Patterns/tree/master/DP_strategy
"전략 패턴"은 클라이언트가 전략을 생성해 전략을 실행할 콘텍스트에 주입하는 패턴이다.
"데코레이터 패턴"은 메서드 호출의 반환 값에 변화를 주기 위해 중간 장식자를 추가하는 패턴이다. [Head First Design Patterns]에서의 정의를 인용해보겠다.
"데코레이터 패턴"에서는 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
샘플 소스는 아주 이해하기 쉽게, 내가 좋아하는 피자를 예로 만들어봤다. 나폴리(이탈리아) 피자를 만드는데, 치즈 토핑(데코레이터, 데코리이션, 장식, 포장 등)을 추가해보겠다.
https://github.com/sieunkr/Design-Patterns/tree/master/DP_decorator
"데코레이터 패턴"은 메서드 호출의 반환 값에 변화를 주기 위해 중간 장식자를 추가하는 패턴
"프록시 패턴"은 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인에 해당하는 객체를 제공하는 패턴이다. [Head First Design Patterns]에서는 아래와 같이 설명을 한다.
프록시 패턴을 이용하면 원격 객체라든가 생성하기 힘든 객체, 보안이 중요한 객체와 같은 다른 객체에 대한 접근을 제어하는 대변자 객체를 만들 수 있습니다.
"프록시 패턴"은 "데코레이터 패턴"과 구현 방법이 같다고 한다. 단, "데코레이터 패턴"은 반환 값에 데코레이션(?) 즉, 장식을 추가하지만, "프록시 패턴"은 장식을 추가하지 않고 반환을 받는다. 사실 이 설명이 쉽게 와 닿지는 않는다. 초보자(?)인 나는 조금 어렵기는 하다. 반환 여부가 패턴의 차이라고 하기에는 뭔가 미흡한 설명이기는 하다. [개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴-최범균님-인투북스]에서는 아래와 같이 설명이 되어있고, 이 글을 보니 조금은 이해가 된다.
위임 기반의 프록시 패턴 구현은 데코레이터 패턴의 구현과 매우 유사한데, 이 두 패턴은 의도에서 분명한 차이를 보인다. 프록시 패턴의 경우 실제 객체에 대한 접근을 제어하는데 초점이 맞춰져 있는 반면에 데코레이터 패턴은 기존 객체의 기능을 확장하는데 초점을 맞추고 있다. 따라서 클래스의 이름을 부여할 때에는 의도에 맞는 단어를 선택해야 한다.
그래서 나는 샘플 예제 소스를 커넥션 관련 소스로 아주 간단하게 구현해봤다. 사용자는 특정 IP로 커넥션을 생성하고, 커넥션 된 이후에는 채널을 생성하는 예제이다. (참고로, 지난번에 작성한 스프링 클라우드 스트림 도입 사례에서, RabbitMQ 연동 시에도 비슷하게 커넥션--> 채널생성-->큐생성 및 바인딩 의 순서의 프로세스이기는 하다..그냥 참고로ㅎㅎ) 어쨌든, 이 예제가 적절한지는 의문이 든다.
혹시라도 이 글을 보는 분 중에서, 물론 제대로 보는 사람이 없겠지만... 잘못된 내용이 있다면 꼭 피드백 부탁드립니다.
해당 샘플 소스는 미완성이다.
프록시(Proxy) 패턴을 잘 활용한 것인지 애매함
채널(Channel)생성 구현을 어디에 넣으면 좋을지 고민 중
Channel 은 인터페이스만 생성하고, 따로 구현하지는 않았다. ProxyConnection 클래스에서 채널 생성을 하는 게 좋을지? 아니면 RealConnection 클래스에서 구현하는 게 좋을지 잘 모르겠다. 나중에 시간이 되면 한 번 더 고민을 해봐야겠다. 일단, 이번에는 이 정도로 넘어가겠다.
https://github.com/sieunkr/Design-Patterns/tree/master/DP_proxy
"프록시 패턴"은 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인에 해당하는 객체를 제공하는 패턴
"템플릿 메서드 패턴"은 상위 클래스에서 공통되는 알고리즘을 구현하고, 각각의 하위 클래스에서 상세 부분을 구현하는 패턴이다. 글이 너무 길어져서... 조금 지치고 있으니 바로 샘플 소스로 넘어가겠다ㅠㅠ
샘플의 예는 웹사이트 구성을 하는 샘플이다. 혹시 ASP.NET 의 마스터 페이지를 사용해본 적이 있는 개발자라면 이해가 더 쉬울 것이다. (참고로 한때는 닷넷 개발자였던 나도, 마스터 페이지를 사용해본 적이 있다. 물론 지금은 뭐하나 제대로 못하는 애매한 개발자가 되어버렸지만..) 마스터 페이지의 개념은 간단하다. 기본 레이아웃의 마스터 페이지가 있고, 그 마스터 페이지를 상속받는 각 페이지에서는 상세 내용을 각각 구현할 수 있다. 보통은 웹사이트의 최상단(헤더라고 부른다), 최하단(푸터라고 부른다) 영역은 대부분 동일할 것이다. 기본 레이아웃에서 최상단, 최하단 영역을 기본으로 생성하고, 가운데 본문 영역만 페이지에 맞게 변경을 해주면 된다.
https://github.com/sieunkr/Design-Patterns/tree/master/DP_templateMethod
"템플릿 콜백 패턴"은 전략 패턴과 유사한데, 전략 패턴의 기본 구현 방법에 콜백(익명 클래스)을 사용하는 방식이다. 스프링 3대 모델 중 하나인 DI(의존성 주입)에서 사용하는 패턴이라고 한다. 시중에 나온 디자인 패턴 책에서는 자세히 다루지 않고 있다. [토비의 스프링 3.1 vol1 - 토비님 - 에이콘] , [스프링 입문을 위한 자바 객체 지향의 원리와 이해 - 김종민님 - 위키북스] 등에서 참고할 수 있다. 개인적으로는 토비님의 책은 나의 기술서적 베스트 5안에 드는 책이다. 정말 좋은 책이니 꼭 한번 읽어보길 바란다.
아무튼 전략 패턴의 예제와 매우 유사하여, 변경된 부분은 아래와 같다.
글이 길어서인지, 글을 쓰면서 집중력이 떨어지고 있다. 소중한 주말에 이렇게 방구석에서 글을 쓰고 있다. 가장 이해하기 어려웠던 두 개의 패턴이 남았는데, 힘을 내서 글을 후딱 마무리하고 저녁 밥하러 가야겠다.
개인적으로는 이해가 어려웠던 패턴이다. "팩토리 메서드 패턴"의 정의는 아래와 같다. 위키백과에서 가져온 정의이다.
팩토리 메서드(Factory method)는 부모(상위) 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며, 자식(하위) 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 하다. 부모(상위) 클래스 코드에 구체 클래스 이름을 감추기 위한 방법으로도 사용한다.
난 아직도 이 글을 100% 제대로 이해를 못하겠다. 팩토리 메서드는 객체를 생성 반환하는 메서드이다! 정도로 이해한 수준이다. 샘플 소스는 커피 만드는 예로 구현해봤는데, 위 정의를 대입해보겠다. 팩토리 클래스에는 커피를 만드는 메서드는 있지만 실제로 어떤 커피를 만드는 결정은 카페라는 서브클래스에서 한다. 즉, 어떤 클래스의 인스턴스를 만들지를 서브클래스에서 결정한다!라고 이해를 하면 된다. 카페라테를 만들지, 카페모카를 만들지는 카페라는 서브클래스에서 결정을 한다는 것이다. 어렵지만 소스를 보고 이해를 하겠다.
여기서 createCoffee가 바로 팩토리 메서드이다! Factory 클래스의 createCoffee()를 구현한다. 위에 설명한 대로, 모카를 만들지, 라테를 만들지는 서브클래스에서 팩토리 메서드에서 결정한다.
다이어그램으로 이해가 잘 안 되어서, 따로 그림을 아래와 같이 그려봤다.
https://github.com/sieunkr/Design-Patterns/tree/master/DP_factoryMethod
"어댑터 패턴"은 중간에 어댑터(변환기)를 통해 호출하는 패턴인데, 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환하는 패턴이다. 어댑터 패턴을 이용하면, 인터페이스 호환성 문제로 같이 쓸 수 없는 클래스를 연결해서 쓸 수 있다. 대부분의 서적, 블로그에서는 이렇게 설명이 되어있지만 소스를 봐야 이해가 된다. [Head First Design Patterns]에서는 오리의 예를 들었는데, 오리 어댑터로 감싼 칠면조 일수도 있다는 예제인데, 적절하기는 하지만 참 골떄리는 예제이다. 해당 글의 샘플 소스는 모두 내 마음대로 작성하기는 했지만, 어댑터 패턴은 조금 어려워서 어떤 예제를 해야 할지 고민이 많이 된다. 블로그 검색해보면 온통 오리 예제 남발이다. 오리&칠면조 예제는 도저히 부끄러워서 글로 남기고 싶지 않아서 이번에도 내 마음대로 작성해봤는데, 내가 작성하였지만 오리 예제보다 더 골 때리는 예제이기는 하다.
내가 만든 예제는 항공사의 셰어 항공에 대한 예이다. 혹시, 비행기 타고 해외여행 갈 때 공동운항으로 비행기 타본 적이 있는가? 간단하게 설명을 하면, 런던 여행으로 가는 비행기를 대한항공에서 표를 예매를 했다고 가정하자. 근데 대한항공과 영국항공이 공동운항 협정을 맺었다고 가정하자. 나는, 런던으로 가는 대한항공 표를 예매했지만, 실제로는 영국항공의 비행기를 타고, 심지어는 승무원도 영국항공의 외국인 승무원으로 가는 것이다. 너무 억지스러운 예제이고, 나도 공동운항을 딱 한 번밖에 안 타봤기 때문에 적절하지 않은 예일수 있다.
자!! 근데, KoreanAirLine 클래스와 BritishAirLine 클래스는 AirLine 인터페이스를 구현했기 때문에, fly라는 메서드에서 실제 핵심 로직을 수행한다. 만약에, AirLine 인터페이스를 구현한 클래스가 아닌 경우에는 어댑터를 어떻게 구현하면 좋을까? 바다를 항해하는 크루즈가 있다고 가정하자.
ABCD라는 이름의 익명의 크루즈 회사가 있다고 가정하자. 비행기를 타고 런던에 가려고 했는데, 크루즈를 타고 간다고 가정하면, 정말 억지스럽지만, 아래와 같이 어댑터를 만들 수 있다.
어댑터는 KoreanAirLine 클래스를 상속받는다. 단, fly 메서드가 실행되면, sail() 메서드를 실행하도록 한다. 서로 전혀 맞지 않을 것 같은 인터페이스인데도, 이렇게 어댑터를 구현하여 배를 타고 런던에 갈 수 있는 것이다...
이번 글에서는 [웹프로그래밍 스터디 - 3.디자인패턴(2)-패턴] 라는 주제로 8개의 디자인 패턴을 정리하였다. 참고 서적은 아래와 같다.
Head First Design Patterns
개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴
스프링 입문을 위한 자바 객체 지향의 원리와 이해
토비의 스프링 3.1
클린 소프트웨어
너무 중요한 주제인데, 이렇게 글 하나로 정리하게 되어서 매우 아쉽게 생각한다. 패턴 예제를 모두 새로 구현하다 보니 시간이 오래 걸리기는 했다. 제대로 구현했는지도 의문이고, 해당 패턴에 적용된 객체지향 원리에 대해서도 완벽하게 이해하지 못한 것은 아쉽게 생각한다. 단, 특정 디자인 패턴에 고집을 부리는 일은 없도록 해야겠다. 해당 시스템에 맞는, 해당 조직에 맞는, 해당 프로젝트에 맞는 최적의 디자인 패턴을 간결하고, 가치 있게 구현하는 것이 중요하다.
지금까지 HTTP, 웹서버, WAS, 디자인 패턴 등에 대해서 정리하였다. 이제 본격적으로 스프링 프레임워크 기반의 웹프로그래밍 공부를 시작하겠다. 다음 글에서는 [Serving Web Content with Spring MVC]라는 주제로 스프링 프레임워크 기반의 웹서비스 구축에 대해서 정리할 예정이다.