Template을 이용한 Observer 패턴 – 1단계

C++의 template을 이용해서 Observer를 구현하는 것에 대한 단계적 설명입니다. 원본은 데브피아에서 김태현(tipani)님이 올려 놓으신 글을 기반으로 작성했으며 한단계 더 업그레이드 했습니다. 제가 늘 느껴오는 것이지만 C++의 template은 기존의 클래스간 관계도에 한정해 볼적에 그 디자인을 획기적으로 개선한다는 점에서 그 판도를 확 바꿀 수 있는 강력한 개념이라고 생각합니다. 아무쪼록 제가 김태현님의 글을 보고 매우 재미있게 template에 한발짝 다가섯듯이 여러분도 제 글을 통해 template에 한발짝 다가 설수있다면 정말 기쁘겠습니다. 참고로 이 글을 읽기 전에 Observer 패턴이 무엇인지 패턴입문서를 살펴보시길 바랍니다. 또한 이 글의 진행은 단계 단계 개선해 나가는 흐름으로 진행된다는 점에 유의하시길 바랍니다.

먼저 1단계입니다. 아래의 코드는 Observer들의 관리에 대한 책임을 맡고 있는 Observed 클래스입니다.

template 
class Observed {
public:
    Observed() {}
    typedef std::list typeObservers;
    virtual ~Observed() {}

    void RegisterObserver(T *pOb) {
        m_listObserver.push_back( pOb );
    }

    void UnRegisterObserver(T *pOb) {
        m_listObserver.remove(pOb);
    }

protected:
    typeObservers m_listObserver;

};

Observed가 관리하는 Observer 클래스인 Observabe 입니다. 단순히 Observed가 호출할 Observer의 OnEvent 함수가 순수가상함수로 선언되어 있습니다.

class Observable {
    virtual void OnEvent(int a) = 0;
};

그리고 아래는 Obserable를 상속받아 구현한 클래스들입니다.

class Observable_A : public Observable
{
    virtual void OnEvent(int a) {
        std::cout << "Fire_A -> " << a << std::endl;
    }
};

class Observable_B : public Observable
{
    virtual void OnEvent(int a) {
        std::cout << "Fire_B -> " << a << std::endl;
    }
};

이제 마지막으로 Observed가 자신이 관리하고 있는 Observable의 OnEvent를 호출해 줘야 하는데, Observed 클래스는 자신이 관리하고 있는 Observable의 타입을 모르기 때문에 Observed와 Observable의 관계를 연결해 주기 위한, Observed로부터 상속받은 EventSrc 클래스가 필요합니다.

class EventSrc : public Observed
{
    void Fire(int a)
    {
        typeObservers::itrator it;
        for(it=m_listObserver.begint(); it!=m_listObserver.end; ++it) {
            (*it)->OnEvent(a);
        }
    }
};

드디어 1단계의 Observer 패턴의 구현이 완성되었습니다. 실제 사용하는 예는 다음과 같습니다.

int main() {
    EventSrc *pES = new EventSrc();
    Observable *pO_A = new Observale_A();
    Observable *pO_B = new Observale_B();

    pES->RegisterObserver(pOA);
    pES->RegisterObserver(pOB);

    pES->Fire(99);


    delete pO_B;
    delete pO_A;
    delete pES;

    return 0;
}

1단계에서 산출된 소스만으로도 충분할 수도 있겠지만, 큰 문제점이 하나 있습니다. 그것은 바로 SRP(Single Responsiblity Principle)를 위반한다는 사실입니다. 즉, Obserable를 관리하는 책임을 Observed와 EventSrc라는 두개의 클래스가 책임을 나눠서 지고 있다는 점입니다. 그럴수밖에 없는 이유는 본문에 언급을 했구요.

이제 다음 2단계 이후부터는 이러한 SRP의 원칙을 지켜나가는 것을 해결문제로 다뤄나가면서 점차적으로 개선된 Observer 패턴을 구현해보겠습니다.

[펌] 아키텍처 체크 리스트

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. 시스템을 구현할 프로그래머로써 당신은 아키텍처에 만족하는가?

출처: Steve McConnell의 Code Complete(MS Press 1995)

이 체크 리스트를 읽은 후, 실제 Code Complete 책을 읽고 내가 느낀 아키텍쳐를 세가지로 나타낸다면 다음과 같다.

  1. 아키텍쳐는 산출물이기 이전에 실무 개발자가 코딩하는 매 순간 순간 마다 참고하고, 참고 해야만 하는 유연한(때로 아키텍쳐도 수정되어야 한다) 성서이여야 한다.
  2. 아키텍쳐는 DBMS와 Web Server 그리고 방화벽 등의 시스템의 구성도와 같은 A2 용지 크기의 한장짜리 문서를 포함해서, 시스템을 개발하기 위해 고려해야할 사항을 세부적으로 기술하거나 묘술하는 것이다.
  3. 아키텍쳐에서 어떤 선택 사항에 대한 이유가 반드시 있어야 한다. “예전에 그랬으므로..” 또는 “다른 시스템도 그랬으므로..” 라는 것은 이유가 될 수 없다.