ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [책]GoF 디자인패턴! 이렇게 활용한다
    나의 서재 2007.06.06 22:30



    GoF 디자인 패턴! 이렇게 활용한다 - 10점
    장세찬 지음/한빛미디어


    이 책은
    Gang of Four(Erich Gamma, Richard Helm, Ralph Johnson, Jone Vlissides)가 쓴 “Design Patterns: Elements of Reusable Object-Oriented Software” 라는 책을 보다 쉽게 접근 할 수 있도록, 다양한 C++과 관련된 문제와 이를 해결하기에 적합한 디자인 패턴을 소개하는 방식으로 이루어져 있습니다.

     

    이 책이 놀라운 점은 저자가 한국인이라는 점입니다. 국내에서는 정말이지 드물게 C++의 아주 깊은 이해를 필요로 하는 분야인 디자인패턴에 대해 이렇게 멋진 책을 쓴 저자가 저와 같은 프로그래머라는 것이 놀랍고 부러울 뿐입니다. 이 책은 거의 500페이지가 넘는 분량으로 GoF 의 어렵고 난해한 패턴을 실제 프로그램에 적용할 수 있도록 많은 예제와 대안제시, 소스 코드 일부 구현, 장단점 분석에 할애하고 있습니다.

     

    저자는 서문에서 본인이 고안한 생각모델인 WWH(WHAT – WHY – HOW)를 소개하고 있습니다.

     

    여기서 ‘WHAT-WHY-HOW’ 생각모델이란, “무엇(WHAT)을 할 것이며, (WHY) 그것을 해야 하나어떻게(HOW) 할 것이며, (WHY) 그렇게 해야 하나라는 질문을 반복해서 구체화시켜나가면 최적의 해결책을 얻을 수 있다는 믿을 기반으로 문제 해결을 위한 고민이나 생각을 해 나가도록 만든 틀을 가리킨다.

    ‘WHAT-WHY-HOW’ 생각모델에 근거해서 단 한 개의 패턴을 읽더라도 무슨(WHAT) 목적으로 왜(WHY) 이런 식(HOW) 의 패턴이 필요하게 되었는가라는 화두를 늘 염두에 두고 패턴을 이해하고, 활용하게 되기를 다시 한번 기대한다.

     


     

    이 책은 패턴자체를 설명하기 전에 문제(WHAT)를 먼저 설명하고, 이 문제를 풀기 위해 어떠한 방법으로 접근해야 하는지(HOW) 간단한 예시를 제시한 다음, 예시와 같은 일반적인 방법으로 구현할 경우 추후 유지보수 등의 문제가 없는지, 왜 이런 식으로 구현했는지 질문을 던지며(WHY), 마지막으로 이 경우에 적합한 GOF의 디자인 패턴을 설명해 주는 방법으로 책을 구성하고 있습니다.

     

    단지 아쉬운 점이 있다면

     

    -       설명하고자 하는 패턴의 수가 20여 개나 되고 각 패턴마다 저자 나름대로 문제 상황을 새로 만들어서인지, 어떤 경우에는 문제가 좀 막연하거나, 패턴을 위해 좀 짜 맞춘 듯 한 경우도 있고,

    -       소스코드는 검증이 덜되어, 눈으로 봐도 코드에 문제가 있는 경우가 있으며(코드가 중요한 것이 아니라, 어떤 경우 이런 식으로 코딩하는냐의 문제이기 때문에 그다지 중요한 문제는 아닙니다)

    -       설명하는 디자인 패턴의 이름이 영어로 되어 있어, 책을 읽고 지나가다 보면 좀 전에 본 패턴이 무슨 패턴인지 사실 잘 기억나지 않는 경우가 많았습니다

    -       또 저자가 해결하는 방법이 저와 생각이 다른 경우도 많아, 왜 이렇게 해결했을까, 이렇게 해결한 경우 이런 문제가 있을 텐데 하는 경우가 몇 가지 있었습니다. (저자가 물론 저보다 훨씬 나은 해결책을 제시했겠지만 말입니다 ^^)

     

    C++ 과 같은 객체지향 프로그램에 있어, 문제를 해결하는데 정답이 있는 경우는 드뭅니다. 디자인패턴을 읽지 않았을 때에도 GoF 가 정의한 디자인 패턴의 이름은 몰랐지만, 오랜 코딩 경험으로 이미 디자인패턴과 동일한 패턴을 적용하여 프로그램하는 경우가 많았기 때문에 이 책을 읽으면서도 크게 어렵거나, 하는 부분은 특별히 없었습니다.

     

    그리고 이 책을 통해

     

    -       그 동안 막연하게 해결했었던 문제들을 보다 체계적으로 정리할 수 있게 되었고

    -       아직 경험하거나, 다뤄보지 못한 경우에 대한 패턴을 학습함으로써 추후 그러한 문제가 있을 경우 대처할 수 있는 내공을 쌓을 수 있어서 큰 도움이 되었습니다.

     

     

    책에 나온 패턴 정리

     

     

    1.     대부분의 패턴들은 가상함수로 이루어진 인터페이스 클래스를 상속받아 구현하는 경우가 많습니다. – 굳이 패턴이 아니라도 C++ 의 창시자가 쓴 The C++ Programming 이라는 책에, 프로그램의 종속성을 없앨 수 있는 2가지로 인터페이스와 Template 이 소개되고 있습니다.

    2.     프로그램에 많은 if 문이나 switch 문이 있다면 아래 나열된 패턴들을 적용할 필요가 있습니다. – if switch 문은 추후 조건이 추가되거나, 삭제 될 때 일일이 해당 소스를 찾아 수정해야 하는 유지보수 부담이 큽니다. 이를 해결하기 위해 조금은 어렵지만, 아래와 같은 패턴을 공부하고 적용하여 유지보수 부담을 줄이는 작업이 필요합니다.
     


    패턴명칭

    설명

    특징

    Abstract Factory

    클래스를 직접 생성하지 않고, 클래스를 생성하는 클래스를 별도로 두어 추가적인 클래스 생성에 대한 부담을 줄여주는 패턴(COM Factory 와 동일)

    -       클라이언트 프로그램이 직접 클래스를 생성하지 않아도 되기 때문에 사용하는 클래스로부터 종속되지 않게 된다.

    Builder

    하나의 소스 객체에서 복잡한 여러 개의 객체를 만들 수 있도록 하는 패턴

    -       복잡한 객체 생성 부분은 클라이언트에게 보이지 않는다

    -       Builder 는 가상 클래스로 정의되고 실제 객체가 하는 일은 Builder 를 상속받은 Concrete 클래스가 맡아 처리한다.

    Factory Method

    클래스를 직접 생성하지 않고, 대행 함수를 통해 간접적으로 객체를 생성하는 방식

    Factory Method 는 상위클래스에서 가상함수로 정의되고 상속받은 하위 클래스에서 실제 객체를 생성하는 역할을 담당

    -       생성할 객체의 자료형이 하위 클래스에 의해 결정됨

    -       하위 클래스들에게 개별 객체의 생성 책임을 분산시켜 객체의 종류별로 객체 생성과 관련된 부분을 국지화 할 수 있다.

    Prototype

    생성할 모든 객체를 미리 만들어 두고, 추후 객체 생성시에는 만들어둔 객체를 복사(Clone)하여 새로운 객체를 생성하는 방법

    Prototype Manager – 복제할 원본객체들을 모아 관리하는 객체

    -       객체를 생성하는 시점에 객체의 자료형을 몰라도 된다(이미 생성한 객체의 복사를 통해 객체 생성, 객체의 자료형을 알기 위해 복잡한 switch 문을 필요로 하지 않는다)

    -       클라이언트 입장에서는 모든 서로 다를 객체를 알 필요 없이 Prototype 인터페이스로만 접근 가능

    -       생성할 객체가 런타임에 결정될 경우 유용하다.

    Singleton

    최대 N 개로 객체생성을 제한하고자 할 경우 사용

    생성자 및 복제 연산자를 private 이나, protected로 선언하여 일반적인 방법으로는 객체가 생성될 수 없게 해야 함.

    -       Singleton 으로 생성할 개체는 대부분 별도 생성을 위한 global 함수나 static 멤버 함수를 통해 생성한다.

    Adapter

    기존 클래스 상속구조와 틀린 인터페이스를 가진 클래스가 신규 추가될 경우 이 클래스를 기존 상속에 묶기 위해 Adapter 클래스로 추가된 클래스를 Wrapping 하는 방법

    Adapter 패턴에는 객체를 참조하는 방식인 Object Adapter 와 다중 상속을 통한 방법인 Class Adapter 가 있음.

    -       기존 클래스를 재사용하려고 하나 그 인터페이스가 원하는 것과 동일하지 않을 때 사용

    -       클라이언트는 새로 추가된 클래스가 기존과 다른 인터페이스를 가지고 있더라도 Adapter 로 인해, 기존 클래스와 동일한 인터페이스를 통해 제어 가능

    Bridge

    외부에 공개되는 인터페이스 및 그에 따른 논리적 관점의 클래스 상속구조와 이들을 구현하기 위한 클래스의 상속구조를 독립적으로 정의하고, 논리적 관점의 클래스에서 구현클래스를 참조하는 형태로 설계된 클래스

    인터페이스 클래스와 그것을 구현해 주는 클래스들의 상속관계를 독립적으로 정의하고 이들간을 마치 다리처럼 연결해주는 형태의 클래스 구조를 가리킨다.

    -       사용용도에 따른 논리적 관점의 클래스 상속구조와 구현 플랫폼 별 클래스 상속구조를 별개로 정의

    -       인터페이스와 구현을 분리시켜 준다.

    -       인터페이스와 실 구현이 서로 복잡하게 연결되는 것을 간소화 할 수 있다.

    -       인터페이스와 구현방식이 각각 서로 다른 형태의 하위 클래스 구조를 가지면서 확장할 수 있다.

    -       인터페이스의 구현이 변경되더라도 클라이언트소스는 재 컴파일이 필요 없다.

    Composite

    하나이상의 객체로 이루어진 복합객체를 기존 객체와 동일한 인터페이스로 접근할 수 있도록 해 주는 패턴(Adapter 와 많이 닮았음)

    -       복합객체는 부품이 되는 단일객체와 동일한 인터페이스를 가짐

    -       클라이언트는 단일 객체와 복합객체를 단일한 인터페이스로 접근

    Decorator

    이미 생성된 객체에 동적으로 신규 기능을 추가하고자 할 경우, 신규 기능을 지원하는 클래스를 linked list 방식으로 계속 해서 추가해 나가는 방식(linked list 에 들어갈 기능들은 모두 동일한 인터페이스여야 함)

    -       특정객체에 동적으로 새로운 기능을 추가/삭제 하고자 할 경우 사용

     

    Facade

    클라이언트가 A>B>C>D 와 같은 순서로 여러 클래스를 호출하여 결과를 얻어야 할 경우, (A>B>C>D) 를 하나로 묶어 처리하는 대표(Facade) 클래스를 정의하여 처리하도록 제공하는 패턴

    -       복잡한 서브시스템에 대해 간단한 인터페이스를 제공하고자 할 경우 유용

    -       클라이언트와 구현클래스간 의존관계를 감소하는 방안으로 사용

    Flyweight

    여러 객체에서 공통으로 사용되는 정보를 그렇지 않은 정보와 분리하고 공유 가능한 정보를 객체형태로 정의해서 정보공유를 수행하는 형태의 설계

    -       객체공유를 통해 자원 사용량을 줄여주기 위한 설계

    -       Factory Method 패턴이나, Singleton 패턴을 사용하여 객체를 생성, 공유한다.

    Proxy

    기본적으로 어떤 역할을 수행하고 있는 클래스가 존재할 때 그 클래스가 제공하는 기능이나 역할을 그대로 활용하면서 부가적인 기능이나 역할을 수행해 주기 위해 새로운 클래스를 정의하고 클라이언트의 모든 요청을 새로 정의한 클래스 객체를 거쳐 원래의 클래스객체에게 전달하는 방식의 클래스 구조

    -       새로 정의한 클래스(Proxy) 는 원래 존재하는 클래스와 동일한 인터페이스를 제공해야 한다. (클라이언트 수정 최소화)

    -       새로 정의한 클래스를 통해 클라이언트 코드 수정 없이 추가적으로 더 많은 일을 수행하는 대행 클래스를 정의 할 수 있다.

    Chain of Responsibility

    객체들간의 관계에 의해 체인을 구성해두고 특정 객체에게 요청이 전달될 경우, 해당 객체가 요청을 처리하지 못하면 체인상에 있는 다른 객체에게 요청을 대신 처리하게 하는 방식의 설계

    -       하나 이상의 객체가 요청을 처리할 수 있고 미리 어떤 객체가 요청을 처리할 지는 알지 못하며, 실제 요청을 처리하는 객체는 자동으로 결정되기를 원할 경우 사용

    -       단점: 요청이 제대로 처리되는지, 처리 시간이 얼마나 걸릴지 알 수 없음.

    Command

    여러가지 요청(Command) 들에 대해 이를 처리해야 하는 클라이언트의 부담을 줄이고 추가/삭제를 용이하게 하기 위해 요청과 요청을 처리할 객체를 중계하기 위한 클래스 상속구조

    Command 클래스는 요청을 처리할 객체를 내부적으로 미리 저장, 관리하고 요청이 들어오면 요청을 처리할 객체의 멤버 함수를 불러주는 역할

    -       미리 map<string, ICommand*> 와 같이 명령문자열과, 실제 이를 처리할 클래스(ICommand 인터페이스 상속받음)를 생성, 관리해야 한다. 

    -       클라이언트는 명령이 들어올 경우 명령을 자동 연결 처리할 클래스에게 바로 위임시킬 수 있다.

    Interpreter

     

    -        

    Iterator

    STL Iterator 를 생각하면 됨.

    데이터 아이템을 가진 데이터 클래스와 이를 navigation 할 수 있는 Iterator 클래스를 정의해서 사용

    -       복잡한 데이터 클래스의 내부를 알 필요 없이 각 항목별로 접근할 때 유용

    -       서로 다른 Aggregate 클래스 객체에 대해 각 항목을 접근하기 위한 인터페이스를 동일하게 정의하고자 할 때

    Mediator

    M 개의 클래스가 각자 별도로 연결될 경우 각 클래스간 연결이 M:N 이 되어 복잡해 질 수 있음. 이를 해결 하기 위해 M 개의 클래스가 직접 연결하지 않고 Mediator 클래스를 통해 서로를 연결하는 방법

    -       객체들간의 복잡한 상호작용이 존재해서 서로간의 의존관계가 불명확하고 이해하기 힘들 때

    -       하나의 객체가 다른 많은 객체들을 참조하거나 상호작용하고 있어 재사용이 힘들 때

    Memento

    객체의 상태 정보를 별도의 클래스로 정의하고, 리스트 자료구조에 그 클래스의 객체를 필요한 시점마다 저장해두었다가 특정 시점에서 객체의 상태복원을 쉽게 할 수 있도록 만든 클래스 구조

    -       어떤 객체의 특정시점에 대한 상태 정보가 나중에 복원되기 위해 저장되어야 할 때 사용

    Observer

    하나의 소스데이터를 여러 곳에서 참조할 경우 소스가 변경됨을 손쉽게 참조하는 클래스로 전달해주는 클래스 구조

    COM EVENT SINK 방식과 유사

    -       Subject 클래스는 자신의 객체에 의해 영향 받을 Observer 객체들을 등록, 삭제할 수 있는 Attach, Detach 멤버 함수를 제공

    State

    어떤 객체의 내부 상태가 계속 변경될 가능성이 있을 때 새로운 상태의 추가도 쉽도록 만들어 주고, 추가된 상태를 포함해서 객체의 상태 변화 시 기존 소스코드 변경 없이 행위 수행 변경이 가능하도록 객체 상태 정보를 클래스 상속구조로 정의해서 사용하는 방식

    -       IState 라는 인터페이스를 정의하고, 상태1, 상태2, 상태3 등을 모두 IState 를 상속받도록 한 다음, 내부상태가 변경될 때마다 상태1 > 상태2 식으로 객체를 변경하여 사용하는 방식

    -       클라이언트는 IState* 만 사용하기 때문에 객체의 상태 변경에 대한 코드 변경 부담이 적음

    Strategy

    여러가지 전략중 하나를 동적으로 적용할 필요가 있을 경우 IStrategy 인터페이스를 정의하고, 이를 상속받는 다양한 전략클래스를 정의한 다음, 클라이언트의 필요에 맞는 전략을 사용하는 방법

    -       서로 행위만 다를 뿐 밀접한 연관을 가진 여러 클래스들에 대해 필요한 시점에 어느 한 행위를 수행하는 클래스를 골라 사용하고자 할 경우 유용

    Template Method

    구체적인 구현은 다르나 큰 틀에서 알고리즘의 기본 골격이 동일한 경우, 동일한 기본 골격을 상위클래스에서 하나의 모듈로 작성, 관리할 수 있게 되는 데 이를 Template Method 라 하고 이를 포함하는 클래스 구조를 Template Method 패턴이라고 함.

    -       A 클래스와 B 클래스 상세 구현은 다르나 하는 일이 거의 같은 경우 공통적인 부분을 하나의 상위 클래스에서 구현하도록 하고 이를 상속받도록 함.

    -       구현부분에 있어 일부 달라지는 부분은 최상위클래스는 인터페이스만 정의하고 하위 클래스가 이를 실제 구현함

    Visitor

    새로운 작업의 추가, 변경이 쉽도록 작업대상과 작업항목을 각각 별도의 클래스 상속구조로 분리해서 정의하고, Double-Dispatch 방식에 따라 작업수행이 일어나게 클래스를 설계한 것

    -       다양한 인터페이스를 가진 객체 여러 개에 대해 그들의 자료형에 따라 각각의 작업을 수행하고 싶을 때

    -       작업대상이 되는 클래스 구조는 확장될 가능성이 없는 대신 수행할 작업 항목은 계속해서 추가, 확장될 소지가 많을 때 유용

     

     

    개인적으로 좋은 책을 읽고 보니, 예전처럼 하루 종일 프로그램 하던 시절로 돌아 가고 싶어 지네요.

    회사 생활 14년 차에 이제는 PM으로, 어중간한 관리자로 코딩 한 줄 없이 보내는 시간이 대부분이라, 잘 풀리지 않던 프로그램을 멋지게 끝내고 느끼던 카타르시스를 맛볼 수 없다는 게 정말 큰 아쉬움입니다.

    이쪽 분야에 대한 관심과 공부를 게을리 하지 않도록 계속해서 열심히 노력해야겠습니다.

    댓글 2

    • 프로필사진

      틈틈히 코딩할 시간을 스스로 확보해야지요..

      2007.06.06 18:35
      • 프로필사진

        그러게요.. 평일은 야근하느라 힘들고 주말이라도 코딩할 수 있는 환경을 만들어(집에서 ㅠㅠ) 계속해서 노력해야 겠습니다.

        2007.06.12 19:36 신고
Designed by black7375.