'pattern'에 해당되는 글 5건

  1. 2007.11.11 Decorator Pattern (3)
  2. 2007.10.24 Flyweight Pattern (2)
  3. 2007.10.23 Composite Pattern (2)
  4. 2007.04.01 Leader/follower pattern
  5. 2007.01.10 Singleton vs Critical section

의도
  객체에 기능을 동적으로 추가/삭제하기 위해 사용. 상속을 이용하지 않고 객체의 합성을 이용하므로 flexiblity가 높다.

동기
  클래스 전체가 아닌 각각의 객체마다 특정한 기능을 추가 시기고자 하는 경우. 예를 들자면 Text view가 있고, 그 text view에 스크롤바나 border를 추가 하고자 한다. 이 스크롤바나 borader 기능은 사용 될 때도 있고 그렇지 않을 때도 있으며, 두 가지 기능이 동시에 사용 될 때도 있고, 한 가지씩 따로 사용 될 때도 있다. 이렇게 다이나믹한 관계를 가지는 기능들을 상속을 이용해서 구현한다면 스크롤바가 첨가 된 text view, border가 첨가 된 text view, 스크롤바와 border가 동시에 첨가된 text view등등 클래스의 수가 폭발적으로 늘어날 가능성이 있고, 새로운 케이스가 발생할 때 마다 새로운 클래스를 만들어야 하는 단점 있다.

  이런 상속의 단점을 해결하기 위해 객체의 합성을 이용 하는, 즉, 추가 되고자 하는 특정 기능을 클래스로 구현하고, 그 인터페이스를 user class가 내포 하므로써 동적으로 추가 기능을 변경 가능토록 하고, 또한 추가 기능 클래스가 다른 추가 기능클래스를 내포하는 재귀적인 형태를 가짐으로써 여러 가지 기능의 추가가 가능토록 할 수 있는 패턴이 Decorator Pattern이다.

구조

사용자 삽입 이미지
참여 객체
 * Component의 역할
   추가 기능이 되는 decorator(border)와 그것을 사용하는 concrete component(text view)의 인터페이스
 * ConcreteComponent
   Component의 인터페이스를 구체적으로 구현
 * Decorator(장식자)
   Component 역할과 동일한 인터페이스를 가짐
   Decorator할 대상이 되는 Component의 역할 또한 가지고 있음
 * ConcrteDecorator(구체적인 장식자)
   구체적인 Decorator의 역할

결과
 - 상속을 이용하여 기능을 추가 하는 것 보다, 합성을 이용하여 구현하므로 보다 높은 유연성을 제공한다.
 - 동일한 기능을 반복하는 것이 간단해 진다.
 - 해당패턴은 복잡한 클래스들을 미리 정의 해 놓으므로써 예측가능한 모든 특성들을 지원하는 것이 아니라 기능의 필요에 따라 '점진적으로 확장'해 나갈 수 있는 형태를 띄고 있다.
 - Decorator pattern은 클래스의 수는 줄어드는 반면 객체의 수는 늘어나는 성향이 있다. 또한 이들 객체는 서로 다른 객체임에도 불구하고 연결된 형태(예를 들어 decorator가 또 다른 decorator를 내포한다던지..)에 따라 비슷하게 보이는 경우도 있다. 따라서 해당 패턴을 적용한 경우 해당 패턴에 대해서 잘 알고 있지 못하면 이해하거나 디버깅하기 어려울 수도 있다.

Decorator, Adapter, Composite 패턴

 이 세 개의 패턴 클래스 간에는 본질적인 유사성이 있다. Adapter 클래스는 기
존의 클래스를 장식하는 역할을 수행하는 것처럼 보인다. 그러나 해당 클래스
의 기능은 특정 프로그램에 대하여 좀더 편리한 클래스의 인터페이스로 변경하
는 작업을 수행하는 것이다. Decorator 클래스는 클래스의 코든 인스턴스보다는
특정 인스턴스에 메소드를 추가한다.
 
여기에서 가능성 있는 상황과 단일 항목으로 구성된 composite 클래스가 본질
적으로는 decorator 클래스라는 것이다. 그러나 다시 말하지만, 의도는 다르다.

(http://blog.naver.com/smurpettt/150000478580)


Ref.
 자바로 배우는 디자인 패턴
 GoF Design Patterns
 Headfirst Design Pattern
 http://blog.naver.com/cosmosb612?Redirect=Log&logNo=80040955894
 http://blog.naver.com/smurpettt/150000478580

'진리는어디에' 카테고리의 다른 글

STL map의 요소 삭제시 주의 할 점  (7) 2007.12.13
volatile - Multithreaded Programmer's Best Friend  (6) 2007.12.05
Decorator Pattern  (3) 2007.11.11
DLL에서 EXE의 함수 호출하기  (1) 2007.11.03
Flyweight Pattern  (2) 2007.10.24
Composite Pattern  (2) 2007.10.23
Posted by kukuta
TAG pattern

댓글을 달아 주세요

  1. Favicon of http://pudae.tistory.com BlogIcon pudae 2007.11.13 15:58  댓글주소  수정/삭제  댓글쓰기

    흥~ 거짓말..

  2. Favicon of http://blog.naver.com/nadau BlogIcon 철우 2007.11.25 14:06  댓글주소  수정/삭제  댓글쓰기

    음메 머 글올릴곳이 없으야... 잘사는지 궁금해서 블러그에 글올린다. 쑹쑹

의도
 작은 크기의 객체들의 수가 폭발적으로 늘어나는 것을 방지하기 위해 공유 개념을 도입.

동기
 객체 지향적으로 설계함으로써 많은 유연성과 재활용성을 기대 할 수 있기는 하지만 늘어만 가는 클래스들을 보고 있노라면 그 비용이 만만치 않음을 느낀다.
 
 클래스를 작은 단위 요소까지 나누어서 설계를 한다면 어플리케이션의 응용성을 높일 수 있고, 해당 요소들을 일관성 있게 다룰 수도 있으며, 요소들에 새로운 기능을 추가하거나 수정한다고 해도 다른 기능에 아무런 영향을 주지 않을 수 있다. 하지만 너무 작은 요소들까지 객체로 다룰때 폭발적으로 늘어나는 객체를 처리 하는 비용은 만만치가 않다. Flyweight 패턴은 이러한 문제를 '공유'를 통해 해결하고자 하는 것이다.

구조

Flyweight_Pattern_Static_Diagram
참여 객체
 * Flyweight
  - extrinsic한 데이터를 받아 들이고, 동작해야 하는 인터페이스를 선언하고 있다.
 * ConcreteFlyweight
  - Flyweight 인터페이스를 구현 하고, intrinsic(변하지 않는, 본질적인) 상태에 대한 저장소를 정의한다.
 * UnsharedConcreteFlyweight
  - 모든 Flyweight서브 클래스들이 공유될 필요는 없기에 이런 클래스가 만들어 졌다고 한다.
  /**
   GoF Design Patterns에서는 공유될 필요가 없는 객체에 대해서도 이렇게 UnsharedConcreteFlyweight 클래스
   를 제공하고 있지만, 개인적으로는 공유될 필요가 없는데이터를 들고 있다면 굳이 Flyweight 인터페이스를 이
   용하여 구현 할 필요성을 못 느끼고 있다.
  */
 
 * FlyweightFactory
  - Flyweight 객체를 생성하고 관리한다. 클라이언트가 팩토리에게 Flyweight객체를 요구하면 존재하는 인스턴스
    를 제공하거나, 존재 하지 않는다면 새로 생성하여 제공 해주어야 한다.
 
구현
 1. 본질적으로 변하지 않는 상태와 각 상황에 따라서 변할 수 있는 부가적 상태를 구분하여 Flyweight에는 본질적 상태에 대한 정보만 저장 하여야 한다.
 2. 공유객체는 Factory를 통해서 생성하도록 한다. 클라이언트는 공유객체의 생성을 요청 할 수만 있을뿐 직접적으로 생성에 관여 해서는 안된다.

결과
 * 공유 객체의 생성을 요청, 생성, 사용 하는 과정에서 실행 시간을 손해 볼 수 있다. 이는 저장 공간과 상충 관계를 갖는다.

Ref.
 Flyweight Pattern : http://blog.naver.com/imzhoon?Redirect=Log&logNo=40039189043
 자바로 배우는 디자인 패턴
 GoF Design Patterns
 Headfirst Design Pattern

'진리는어디에' 카테고리의 다른 글

Decorator Pattern  (3) 2007.11.11
DLL에서 EXE의 함수 호출하기  (1) 2007.11.03
Flyweight Pattern  (2) 2007.10.24
Composite Pattern  (2) 2007.10.23
RST 메시지  (0) 2007.10.10
XML 기초  (9) 2007.10.04
Posted by kukuta
TAG pattern

댓글을 달아 주세요

  1. Favicon of http://pudae.tistory.com BlogIcon pudae 2007.10.24 01:36  댓글주소  수정/삭제  댓글쓰기

    흥~ 거짓말..

의도
 '전체 부분'을 표현하기 위해 객체들을 트리구조로 묶을 수 있다. 클라이언트 코드는 각 개별적인 객체(부분)와 다른 객체를 포함하는 객체(전체)를 동일한 인터페이스를 통해 사용할 수 있다.

구조

사용자 삽입 이미지


 위의 모델에서 Composite 클래스는 Component 인터페이스를 포함하는 형식으로, 실제적으로 Component 인터페이스에는 Leaf나 Composite의 인스턴스를 가리키게 된다. 이런 구조를 인스턴스 다이어그램으로 표현 하면 아래와 같다.
사용자 삽입 이미지

 하나의 집합객체(Composite)가 Leaf나 Composite를 포함하고 그것은 재귀적으로 계속 이어진다. 모든 인스턴스들은 Component 인터페이스를 구현하고 있기 때문에 클라이언트에서는 Leaf와 Composite에 구분없이 동일하게 취급이 가능하게 된다.

역할 
 *  Component
  - Leaf와 Composite를 동일하게 취급하기 위한 인터페이스 정의
  - 전체 클래스에 속한 요소들을 관리하는데 필요한 인터페이스 정의
  - 순환(recursive) 구조에서 요소들을 포함하는 전체 클래스로의 접근에 필요한 인터페이스를 정의 한다.
 * Leaf
  - ‘내용물을 나타내는 역할, 이 안에는 다른 내용물을 넣을 수 는 없다. 파일 디렉토리 관계에서 보면 파일에 해당한다.
 * Composite
  - '집합체' 역할, 이 안에는 내용물과 다른 집합체가 포함될 수 있다. 파일과 디렉토리 관계에서 디렉토리에 해당한다.
 *
 Client의 역할
  - Composite 패턴의 이용자.

구현 관련 신경 쓸 사항
 1. 부모에 대한 참조자를 유지하면 구조를 거슬러 올라가거나 요소를 삭제 하는 과정을 좀 더 쉽게 만든다.
 2. Component의 인터페이스를 최대화 한다. 하지만 Leaf에는 필요 없는 오퍼레이션이 인터페이스에 정의 되어 구조를 파악하는데 방해가 될 수 있다. 그럼에도 불구하고 Leaf와 Composit에 상관 없이 동일한 처리를 하기 위해서 인터페이스에 최대한 많은 오퍼레이션을 정의 한다.
 3. 성능 개선을 위한 캐싱. 자식들에 대한 정보의 일부를 부모가 캐싱하고 있으므로써 자식노드 까지 탐색할 필요 성을 줄여 준다. 하지만 해당 연산 작업이 집중적으로 수행되고 기본데이터가 변경되지 않는다는 것을 확신 할 수 없을 경우 이러한 기능이 필요 없을 수도 있다.
 
결론
 Composite 패턴은 단순 객체 및 복잡한 객체의 계층 구조에 대한 클래스를 정리할 수 있게 하여 클라이언트의 프로그램에서는 모두 같은 종류로 나타낸다. 노드와 리프가 같은 방식으로 관리되기 때문에 이렇게 단순화해서 클라이언트도 그것에 맞추어 간단해질 수 있다.

 Composite 패턴은 개발자의 컬렉션에 새로운 컴포넌트를 추가하기가 매우 쉬운데, 해당 패턴이 유사한 프로그램밍 인터페이스를 지원하는 한 이것은 계속 유효하다. 반면에 Composite 패턴은 시스템을 지나치게 일반화한다는 단점이 있다. 이런 단점이 일반적으로 바람직하게 용인되는 경우 아마도 특정 클래스를 제한할 때 어려움을 겪게 될 것 이다.

Ref.
 Composite 패턴 정리 : http://blog.naver.com/smurpettt?Redirect=Log&logNo=150000478576
 Composite Message 패턴 : http://blog.naver.com/only2u4u?Redirect=Log&logNo=120042169519
 GoF Design Patterns
 Java 언어로 배우는 디자인 패턴 입문
 Headfirst Design pattern

'진리는어디에' 카테고리의 다른 글

DLL에서 EXE의 함수 호출하기  (1) 2007.11.03
Flyweight Pattern  (2) 2007.10.24
Composite Pattern  (2) 2007.10.23
RST 메시지  (0) 2007.10.10
XML 기초  (9) 2007.10.04
정규 표현식(Regular Expressions)  (0) 2007.08.31
Posted by kukuta
TAG pattern

댓글을 달아 주세요

  1. Favicon of http://pudae.tistory.com BlogIcon pudae 2007.10.24 01:37  댓글주소  수정/삭제  댓글쓰기

    흥~ 거짓말..

  얼마전에 libevent가 threadsafe 하지 않다는 글을 대충 번역 해서 올려 놓았더니, pudae가 leader/follower pattern이라는 것을 소개 해줬다. 인터넷 검색 결과 ACE framework에서 사용 되는 서버패턴의 일종이라는데 아직 구현을 해보지 않아서 성능이 얼마나 나올는지는 장담하지 못한다.

  기존의 내가 구현 하려고 했던 형식은 이벤트(accept/read ..etc)를 감시하는 thread가 하나 있고, 이벤트가 발생하며 event queue에 그 이벤트들을 집어 넣는다. 그럼 event queue 반대편의 worker thread들이 그 이벤트에 맞는 핸들링을 하는 구조 였는데 오버헤드가 많아서 실질적으로 사용하기는 다소 무리가 있는 형식 이었다.
 
  그에 반에 leader/follower pattern은 이벤트를 대기 하고 있는 leadther thread, 아무것도 하지 않고 자신의 차례가 오길 기다리는 follower thread로 구성된다. leader thread가 이벤트를 대기 하다 이벤트가 발생하면 leader thread는 processing thread가 되어 event를 처리하러 가고, 대기 하고 있던 follower thread중에 하나가 leader가 된다. 그리고 processing thread가 제 할일을 다 끝내고 나면 다시 follower thread가 되어 자신의 차례가 올 때 까지 대기하게 된다.

  지금 고민하고 있는 것은 follower thread들이 어디엔가 저장 되어 있어야 한다는 것이고, 내 생각에는 그런 자료구조로는 queue가 적당하지 않겠냐는 것이다. enqueue와 dequeue를 할 때 당연히 lock을 걸어줘야 할 것인데..그러면 이 것이 event queue를 운용할 때와 과연 얼마나 차이가 날까 하느냐 이다. 물론 event queue를 사용할 때는 하나의 이벤트가 발생할때 마다 이벤트 객체를 동적으로 생성 했기에 그에 대한 overhead가 있었을지 모르지만 그게 그렇게 크다고는 생각되지 않는다(잘 못 생각하는 것인가??).

  이런 저런 것을 비교해 놓고, 이런 방법을 사용하면 이런면에 있어서 오버헤드가 줄어들기 때문에 성능이 월등히 좋아 진다..라는 문서라도 있으면 좋으련만..결국은 처음 부터 끝까지 구현해 보고 서버들을 벤치 마킹 하는 방법 밖에는 없을 것같다.

  나중에 시간이 나면 one connection - one thread(가칭), event queue(가칭), leader/follower pattern을 서로 비교 해 보고 성능 평가 보고서를 한장 썼으면 한다.

참고 : http://deuce.doc.wustl.edu/doc/pspdfs/lf.pdf

'진리는어디에' 카테고리의 다른 글

간단한 에코 서버 제작  (2) 2007.04.06
도메인 이름을 이용해 IP 주소 얻기(gethostbyname)  (3) 2007.04.03
Leader/follower pattern  (0) 2007.04.01
Condition Variables  (1) 2007.03.25
sendto & recvfrom  (0) 2007.03.23
half-close  (0) 2007.03.20
Posted by kukuta

댓글을 달아 주세요

상황 :
 간단한 이벤트 큐를 만들어야 하는 상황이 발생했다.
 각종 read/write 작업들을 싱글톤 이벤트 큐에 집어 넣고, 몇개의 Thread들이 큐를 감시하다, 큐에 새로운 이벤트가 들어 오면 이벤트에 따라 적적한 작업을 해주는 방식이었다.
 

시간

ThreadA

ThreadB

1

if(queue.empty()) -> not empty

 

2

 

if(queue.empty())-> not empty too

3

queue.pop()

 

4

 

queue.pop() -> 큐는 이미 비었다 Error!!


 위와 같은 문제 때문에 Thread 가 큐에서 pop을 하는 작업은 동기화를 걸어 주어야 했다.
 
 구조는 아래와 같았다.
 getInstance() 함수로 EventQueue의 생성자가 가 호출 되면 생성자 내부에서 WorkerThread들을 호출 하고, WorkerThread들은 EventQueue를 루프를 돌면서 확인한다(지면상 간단히 상상이 가능한 부분은 생략 하도록 한다. 별로 중요한 부분은 아니니 대충 보고 넘어가도 된다.)

queue.h

class EventQueue {
    public :
        virtual ~EventQueue();
        /**
          Singleton 객체 생성
        */
        static EventQueue* getInstance() {
            if(m_selfInstance == NULL) {
                m_selfInstance = new EventQueue();
            }
            return m_selfInstance;
        }

        bool push(EVENT event); // 구현 생략
        EVENT pop();                 // 구현 생략
        bool empty();                  // 구현 생략, 비었으면 true, 그렇지 않으면 false

    private :
        EventQueue() {
            pthread_mutex_init(&m_mutex, NULL);
            for(int i=0; i<WORKERTHREAD_COUNT; i++) {
                m_thread[i].init(&m_mutex);  // thread에게 뮤텍스의 포인터를 넘겨 준다.
                m_thread[i].start();              // create_thread 류의 함수를 호출하여 thread를 동작 시킨다.
            }
        }

        typedef std::queue<EVENT> EVENT_QUEUE;

        static EventQueue*  m_selfInstance;
        EventWorkerThread   m_thread[WORKERTHREAD_COUNT];
        pthread_mutex_t     m_mutex;
};

thread.cpp
void EventWorkerThread::init(pthread_mutex_t* mutex) {
    m_mutex = mutex; //threa의 멤버 변수에 EventQueue의 mutex 변수 포인터를 넘겨 준다.
                             // 이로써 mutux를 공유 할 수 있다.
}

/**
  실제 thread가 작업을 하는 부분이다.
  create_thread 라던지 그런것은 다 접어 두고, 그냥 thread가 이런 작업을 하게끔 되어 있구나 라고 생각하자.
*/
void* EventWorkerThread::run() {
    while(1) {
        /* critical section.. 동기화가 제대로 된다면 1 : ...., 2 : .... 의 형식으로 나와야 한다.*/
        pthread_mutex_lock(m_mutex);  // EventQueue로 부터 넘겨 받아온 mutex 포인터다
        std::cout << "1 : " << (int)this << ":" << (int)m_mutex << std::endl;
        if(!EventQueue::getInstance()->empty()) {
            m_event = EventQueue::getInstance()->pop();
        }
        std::cout << "2 : " << (int)this << ":" << (int)m_mutex << std::endl;
        pthread_mutex_unlock(m_mutex);

        if(m_event.type & EVENT_WRITE) {
              // 이벤트가 발생 하면 뭔가를 하긴 한다.
        }
    }
}

문제 :
  동기화가 전혀 안되고 있다.

원인 :
  Singleton과 그 singleton 객체를 참조하는 Thread 군이 문제!!
  시나리오를 보도록 하자(지금 부터 중요!!).
 
1.  어딘가에서  EventQueue에 뭔가를 넣기 위해 singleton 객체를 호출 한다고 하자.
  static EventQueue* getInstance() {
        if(m_selfInstance == NULL) {
            m_selfInstance = new EventQueue();   //현재 프로세스가 여기 까지 왔다.
                                                                   // if(m_selfInstance == NULL) 를 통과 했으므로
                                                                   // 현재 객체가 없는 상태라고 판단 했다는 것을 잊지 말아야 한다.
                                                                   //그러면 이제 생성자를 보도록 하자.
        }
        return m_selfInstance;
  }

2. 쓰레드 생성
EventQueue() {
    pthread_mutex_init(&m_mutex, NULL);
    for(int i=0; i<WORKERTHREAD_COUNT; i++) {
        m_thread[i].init(&m_mutex);  // thread에게 뮤텍스의 포인터를 넘겨 준다.
        m_thread[i].start();              // 이때 thread가 생성 된다!!
                                                 // 생성 되고 난후 바로 EventQueu를 참조하는 것이 문제!!
    }
}

3. WokerThread가 EventQueue 감시
pthread_mutex_lock(m_mutex);  // EventQueue로 부터 넘겨 받아온 mutex 포인터다
std::cout << "1 : " << (int)this << ":" << (int)m_mutex << std::endl;
if(!EventQueue::getInstance()->empty()) {
    m_event = EventQueue::getInstance()->pop();
}
std::cout << "2 : " << (int)this << ":" << (int)m_mutex << std::endl;
pthread_mutex_unlock(m_mutex);

 굵은 글씨로 되어 있는 부분에 주목하자!! 자 다시 getInstance()를 호출하고 있다. 아까 1번 과정에서 아직 EventQueue 객체가 할당 되지 않은 상태라고 판단하고 있다는 사실을 잊지 말자!! 여기서 EventQueue의 또 다른 객체가 생성되고 또 다른 mutex 객체가 생성된다.

해결 :
  생성자에 lock을 걸어 주던지, getInstance가 호출 완료 되고 난뒤에 thread를 초기화 하는 모듈을 호출하거나.

오늘의 교훈 :
  singleton을 남발 하지 말자.
  thread safe하게 코드를 읽는 버릇을 들이자.
  thread는 무섭다.

참고 :
  오늘도 우리의 pudae 님께서 도와 주셨다..부끄러울 따름이다.

email :
 kukuta@gmail.com

'진리는어디에' 카테고리의 다른 글

extern  (0) 2007.02.23
소켓 강제 종료시 파이프(pipe) 깨짐  (0) 2007.01.27
Singleton vs Critical section  (0) 2007.01.10
TIME_WAIT state vs SO_REUSEADDR option  (4) 2007.01.08
Edge trigger vs Level trigger  (2) 2007.01.05
const vs mutable  (4) 2006.12.21
Posted by kukuta
TAG C/C++, pattern

댓글을 달아 주세요