책임 연쇠( chain of Responsibility )

Posted 2012. 10. 23. 17:42

메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴.

하나의 요청에 대한 처리가 반드시 한 객체에서만 이루어지지 않고, 여러 객체에게 그 처리의 기회를

주려는 것이다. 즉, 이 패턴에서는 하나의 서비스 처리를 여러 객체에 나눌 수 있도록 한다.

메시지를 수신하여 처리를 담당할 객체들을 하나의 연결 고리로 만들고, 실제로 요청을 처리하는

객체를 만날 때까지 계속해서 요청을 전달하게 한다.



#include <windows.h>

#include <tchar.h>

#include <memory>

#include <iostream>

#include <deque>

#include <list>

#include <vector>

#include <algorithm>

#include <iomanip>


class Handler

{

public:

typedef int Topic;

public:

explicit Handler (Handler *s=0, Topic t=0) : 

Successor_(s),

Topic_    (t)

{

}

virtual ~Handler ()

{

}

public:

virtual void HandleRequest(void)

{

if (Successor_)

{

Successor_->HandleRequest ();

}

else

{

std::cout << "Handler::HandleRequest() : no Successor_" << std::endl;

}

}

virtual void SetHandler (Handler *s=0, Topic t=0)

{

Successor_=s;

Topic_    =t;

}

protected:

Handler* Successor_;

Topic    Topic_;

};

class ConcreteHandler1 : public Handler

{

public:

ConcreteHandler1 (Handler *s=0, Topic t=0) : 

 Handler(s,t)

 {

 }

public:

virtual void HandleRequest(void)

{

if (Topic_==1)

{

std::cout << "ConcreteHandler1::HandleRequest()" << std::endl;

}

else

{

Handler::HandleRequest();

}

};

};

class ConcreteHandler2 : public Handler

{

public:

ConcreteHandler2 (Handler *s=0, Topic t=0) : 

 Handler(s,t)

 {

 }

public:

virtual void HandleRequest(void)

{

if (Topic_==2)

{

std::cout << "ConcreteHandler2::HandleRequest()" << std::endl;

}

else

{

Handler::HandleRequest();

}

};

};

class ConcreteChildHandler : public ConcreteHandler2

{

public:

ConcreteChildHandler (Handler *s=0, Topic t=0) : 

 ConcreteHandler2(s,t)

 {

 }

public:

virtual void HandleRequest(void)

{

if (Topic_==3)

{

std::cout << "ConcreteChildHandler::HandleRequest()" << std::endl;

}

else

{

ConcreteHandler2::HandleRequest();

}

};

};



int _tmain(int argc, _TCHAR* argv[])

{

Handler* handler1 = new Handler              ();

Handler* handler2 = new ConcreteHandler1     (handler1, 1);

Handler* handler3 = new ConcreteHandler2     (handler2, 1);

Handler* handler4 = new ConcreteChildHandler (handler3, 2);

Handler* handler5 = new ConcreteChildHandler (handler3, 3);

handler1->HandleRequest();

handler2->HandleRequest();

handler3->HandleRequest();

handler4->HandleRequest();

handler5->HandleRequest();

delete handler1;

delete handler2;

delete handler3;

delete handler4;

delete handler5;

return 0;

}

-> Handler::HandleRequest() : no Successor_
-> ConcreteHandler1::HandleRequest()
-> ConcreteHandler1::HandleRequest()
-> ConcreteHandler2::HandleRequest()
-> ConcreteChildHandler::HandleRequest()

구조 패턴들의 정리

Posted 2012. 10. 23. 17:23

적응자( Adapter) 대 가교(Bridge) 패턴

이둘은 닮았다 .어떤 클래스의 인터페이스 역할을 해주는 면에서 닮았다.

하지만 두 패턴은 그 목적에 차이가 있다. 적응자는 한 클래스의 인터페이스를 현재 적응시키려는

인터페이스에 연결하는 것에 목적이 있기 때문에 인터페이스의 변형이나 발전에는 신경쓰지 않는다.

이에 반해 가교 패턴은 추상적 개념과 구현부를 따로 만들고 이들을 서로 연결시켜주며 시스템의

진화에 따라 새로운 구현의 추가를 고려한다. 

적응자는 설계가 완료된 후, 가교는 설계가 완료되기 이전에 적용 된다.



복합체( composite ), 장식자( Decorator ), 프록시( Proxy )

이들 세가지 패턴은 자신의 부모타임의 객체를 클래스내에 소유한다는 점에서 비슷 하다.

하지만 역시나 목적이 다르다 .복합체 패턴은 실제 객체와 그 객체들을 담는 컨테이너 객체를 같은

타입으로 다룰 수 있게 하여 인터페이스를 일관되게 하는게 목적이다. 이에 반해 장식자 패턴은

인터페이스의 일관성도 있겠지만 그보다도 객체에 대한 추가 기능을 동적으로 추가하여 객체를

합성할 수 있게 하는 것이 그 목적이다.

프록시 패턴은 어떤 추가적인 행동을 첨가하는데서 장식자 패턴과 비슷하지만 프록시는 클래스에

기능을 추가하여 더 무겁게 하는 것이 아닌 제어에 있어서 더욱 용이하게 하는 패턴이다.

플라이급 패턴은 객체에 대한 공유를 가능하게 하는 패턴으로 같은 객체의 중복된 사용을 그 수만큼

생성하는 것이 아니라 플라이급 공장 객체를 통해 필요한 객체를 제공받는 형태이다.

프록시( Proxy )

Posted 2012. 10. 23. 17:16

본래 객체의 대리 객체 패턴.

즉, 참조 카운팅 포인터 객체이다.

복잡한 객체를 여러개 복사해야 하는 상황이 있다면 어떻게 보면 flyweight와 비슷 하다.

둘다 원본 복합 객체에 대한 참조를 포함한다.


그림이 8장이 있다 치면. 현재 1개의 그림만 보이면 되고

이후 나올 가능성이 있는 7개는 꼭 미리 로드 할 필요가 없다.

물론 메모리가 넉넉하고 로드 비용이 없다고 가정하면 미리 로드 하는것이 좋겠지만

메모리는 한정되어 있고 로드와 해제에 드는 비용도 있으므로 보이는 부부만 그리는게

더 좋은 방법이다.

하지만 문제는 이미지 객체를 할당해놓고 이미지를 실제 할당하지 않아야 하는데. 이미지 객체를

직접 할당하면 할당시 로드를 하게 되는 것이므로 다른 방법이 필요하다. 이때 필요한 것이

대리 객체인 프록시 객체이다.


위 그림을 보면 이미지 프록시 객체가 있는데 그 객체를 이용한다는 뜻이다.

위 그림에는 나와있지 않지만 프록시 객체를 생성시 실제 용량을 차지하는 이미지를 로드하는 코드가

생성자에 있지 않아서 결과적으로 로드가 이루어지지 않은 가상의 이미지 객체가 된다.

이런 식으로 문서의 이미지를 객체화 시켜놓고 실제 모니터 출력 부분에 속할때 Draw명령이 떨어지고

Draw명령 속에서 이미지를 생성하고 그 이미지의 Draw명령을 수행한다.

이것은 전문 용어로 Copy-On-Write라는 기법인데 간단히 말해서 "쓸때쓰자" 이다.

사실 사본의 복사 시기에 관련된 용어이지만 이 상황에도 어울리는 용어다.

Draw가 될때만 이미지를 생성해서 그 이미지를 그리는 실제 이미지가 없어도 문서는 가상 이미지 객체

(프록시 객체)를 생성했으므로 이미지가 있는것으로 취급하며 이미지가 실제로 필요할때 생성하는 유

용한 기법이다.

그림대로 코드를 작성하면

// 문서 생성

TextDocument* text = new TextDocument;

text->Insert( new 이미지Proxy("그림1"));

이렇게 문서에 프록시 형태로 생성하여 삽입해놓으면 초기 할당 비용도 아끼고 메모리도 아낀것이 됨


프록시 객체는 이외에도 여러 다른 역할을 수행 할 수 있습니다.

1. 원격지 프록시 : 현재 공간과는 다른 공간에 존재하는 객체를 대리하는 프록시로써, 현재 공간에 존재하게 된다. 즉, 현재 공간에서 프록시 객체에게 명령을 내리면 프록시 객체는 원격지의 실제 객체에게 명령을 전달해주는 역할을 하게 된다.


2. 가상 프록시 : 방금 위에서 살펴 본 문서 안의 이미지 프록시 객체가 가상 프록시 이다. 간단히 말해 요청이 있을때에만 고비용 객체를 생성 한다.


3. 보호용 프록시 : 방금 위에서 살펴 본 문서 안의 이미지 프록시 객체가 가상 프록시 이다. 간단히 말해 요청이 있을때에만 고비용 객체를 생성한다.


4. 스마트참조자 : 프록시는 어떤 면에서는 포인터의 역할과도 비슷하다. 어떤 객체에 대한 참조자를 단순히 포인터로 지정하지 않고 부가적인 기능이 있는 똑똑한 참조자의 역할로 수행하게 할 수 있다.참조 횟수를 저장하여 객체의 할당, 해제를 자동으로 한다든지, 처음 참조 되는 시점에 메모리에 할당하게 한다든지, 실제 객체에 접근하기 전에 병행 제어를 위해 객체에 대해 잠금을 걸 수 있다.


class RealImage

{

private:

m_id;

public:

RealImage(int i)

{

m_id = i;

cout << "    $ $ ctor : " << m_id << '\n';

}

~RealImage()

{

cout << "    dtor : " << m_id << '\n';

}

void draw()

{

cout << "     drawing image  " << m_id << '\n";

}

};


class Image

{

private:

RealImage *m_the_real_thing;

int m_id;

static int s_next;

public:

Image()

{

m_id = s_next++;

m_the_real_thing = 0;

}

~Image()

{

delete m_the_real_thing;

}

void draw()

{

if( !m_the_real_thing )

m_the_real_thing = new RealImage( m_id );

m_the_real_thing->draw();

}

};

int Image::s_next = 1;


void main()

{    

Image images[5];

for( int i ; true ; )

{

cout << "Exit[0], Image[1-5] : ";

cint >> i;

if( i == 0 )

break;

images[i - 1].draw();

}

}

->Exit[0], Image[1-5] : 2

->    $$ ctor : 2

->    drawing image 2

->Exit[0], Image[1-5] : 4

->    $$ ctor : 4

->    drawing image 4

-> Exit[0], Image[1-5] : 2

->    drawing image 2

-> Exit[0], Image[1-5] : 0

->    dtor : 4

->    dtor : 2

« PREV : 1 : ··· : 34 : 35 : 36 : 37 : 38 : 39 : 40 : ··· : 77 : NEXT »