블로그 이미지

 

 

Command 패턴 

 

실행할 명령을 클래스 묶음으로 만들어 실행할 객체에 명령들을 전송한다

함수로 명령을 실행하지 않고 클래스로 만들어 명령을 전송한다는게 핵심인데

 

한가지 더 핵심은 액터가 그냥 자기 자신 내에서 자신의 상태만 업데이트 할 수 있게 한다는 

부가적인 개념을 더한다면 딱이다

 

이를 잘만 활용하면 실행 취소 재실행 을 만들 수도 있다

또한 키보드 바인딩에 사용 할 수도 있다

 

 

 

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<iostream>
#include<string>
#include<vector>
 
 
class Actor
{
public:
    virtual void jump() {
        std::cout << _who << "\t";
    }
 
    virtual void Attack() {
        std::cout << _who << "\t";
    }
 
    virtual void Walk(float speed) {
        std::cout << _who << speed << "만큼 \t";
    }
    std::string _who;
};
 
class Player : public Actor
{
public:
    Player() {
        _who = "Player";
    }
    void jump() {
        Actor::jump();
        std::cout << "점프" << std::endl;
    }
 
    void Attack() {
        Actor::Attack();
        std::cout << "공격" << std::endl;
    }
 
void Walk(float speed) {
Actor::Walk(speed);
        std::cout << "걷기" << std::endl;
    }
 
};
 
class AI : public Actor
{
public :
    AI() {
        _who = "AI";
    }
    void jump() {
        Actor::jump();
        std::cout << "점프" << std::endl;
    }
 
    void Attack() {
        Actor::Attack();
        std::cout << "공격" << std::endl;
    }
 
void Walk(float speed) {
Actor::Walk(speed);
        std::cout << "걷기" << std::endl;
    }
 
};
 
cs

 

 

 

 

여기 부터는 명령들을 클래스화 시켜서 추상화

 

부모 클래스에만 virtual  붙였다고 구조가 복잡해지면 자식함수가 불리지 않는다는 

몰상식한 발언은 삼가합시다

이런 사람의 케이스는 구조가 복잡해져서 호출이 안되는게 아니라 어딘가 잘못 만들어 놓고

왜 호출이 안되는지 이해를 못하는 경우입니다 ( more practice with C++ )

(예전에 이런 무식한 발언을 한 사람이 있었기에 생각나서 적어봄... 그냥 흘려 들으시길..)

virtual 을 자식에 붙이는건 그냥 관용적인 표현임..

 

 

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Command
{
public :
    virtual void execute(Actor* actor) = 0;
};
 
class JumpCommand : public Command
{
public:
    void execute(Actor* actor)
    {
        actor->jump();
    }
};
 
class AttackCommand : public Command
{
public:
    void execute(Actor* actor)
    {
        actor->Attack();
    }
};
 
class WalkCommand : public Command
{
public:
WalkCommand(float speed) : _speed(speed)
{
}
    void execute(Actor* actor)
    {
        actor->walk(_speed);
    }
float _speed;
};
 
 
//여기부트는 메인 구문
int main()
{
 
    //명령들을 한데 모읍니다
    std::vector<Command*> commandNodes;
 
    JumpCommand ji;
    AttackCommand ai;
    commandNodes.push_back(&ji);
    commandNodes.push_back(&ai);
 
    //이련 명령들을 쌓아놓으면 실행 취소와 재 실행 또한 구현 할 수 있다
 
 
    Player pIns;
    AI aIns;
 
    for each (auto node in commandNodes)
    {
        node->execute(&pIns);        //액터에 명령 전송
    }
 
    std::cout << std::endl;
 
    for each (auto node in commandNodes)
    {
        node->execute(&aIns);
    }
    
 
    return 0;
}
 
cs

 

 


 

 

 

 

실행 결과

 

Player  점프

Player  공격

 

AI      점프

AI      공격

 

 

Actor 에 어떤 동작이 있다는 것이 정해져 있고 실행할 명령들을 모아 한번에 처리 ㄱ ㄱ

 

 

 

 

실행 취소는 

 

 

class DoCommand
{
public :
    virtual void do(Actor* actor) = 0;
};

 

 

class UnDoCommand
{
public :
    virtual void undo(Actor* actor) = 0;
};
 

 

 

 

 

여기서 알 수 있는 것은 Command 를 상속 받는 자식 Command 클래스들은 내부를 좀 더 자유롭게 작성 할 수 있다는 것을 알 수 있게 된다,

 

그래서

 

의 Command 들이 있다고 했을때 DoCommand 를 통과하명 명령이 실행 되고

 

UnDoCommand 를 명령어들이 통과되면 반대로 실행 되도록 하위 클래스에서 인자 값들을 조정 구현 해 주면 된다

 

 

 

 

또한 이렇게 두가지 Command 를 두는것도 있지만 명령들을 한대 놔두고 새로운 인스턴스 Actor 를 만들어 그동안 쌓아왔던 명령들을 원하는 부분까지 실행 시키게 하면 그것또한 실행 취소가 가능한 방법이다

 

 

함수 인자를 (람다 ~ Closure 클로저) 로 처리하여 좀 더 유연하게 처리 할 수도 있다

관련 글 http://3dmpengines.tistory.com/1593

 

 

 

반응형

 

 

componentSystem.pdf
다운로드

 

Component System

 

v상속으로 인한 문제점을 피하면서 컴포넌트 조립식 프로그래밍을 하려면...?
è포함 기법 사용
è
v간단히, 필요한 클래스들을 멤버 변수로 잡아 포함할 수도 있겠지만 (하드코딩)
v다수의 컴포넌트 클래스들을 포함할 수 있고,
v컴포넌트 간의 통신이 가능할 수 있도록
v표준적인 방법을 구축하는 것

 

è컴포넌트 시스템 (GPG 64.6)

 

 

이런 개념..

 

 

 

Entity

{

Component A

Component B

Component C

...

}

 

 

è컴포넌트를 interface 로 정의하고 ID 를 부여한다.
èEntity는 ID 당 1개의 컴포넌트만 가질 수 있다.

 

è동시에 두 개 이상 컴포넌트가 필요하다면 그들을 포함하는 새로운 컴포넌트를 개발한다.
 
v기능상 비슷하지만 똑같지 않은 컴포넌트들을 Family (Category) 로 분류할 수 있다.
v같은 분류의 컴포넌트는 다른 것으로 교체 가능해야 한다
(한 분류 내에 후보가 유일하여 무언가 교체해볼 여지가 없다면 컴포넌트 프로그래밍이라 할 수 없을 듯)
 
 
 
v컴포넌트간의 통신
è모든 컴포넌트가 Owner (Parent) 의 포인터를 저장해서 활용함 (예제 코드 참조)
 
 

 

v상속 없이 GameObject 클래스 1개로 게임내 다양한 오브젝트를 표현

 

v가지고 있는 컴포넌트의 종류에 따라 오브젝트의 성격이 변함

 

v상속의 경우 해당 클래스를 가지고 있는지 dynamic_cast 를 썼겠지만,

 

v포함된 컴포넌트는 Family ID에 따라 바로 static_cast (C스타일 캐스팅)로 변환하여 쓸 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

컴포넌트의 사용

 

 

 

 

 

컴포넌트간의 통신

 

 

 

 

 

 

https://t1.daumcdn.net/cfile/tistory/2461294B529BEDCF37?download

반응형

 Adaptor 패턴


1. Adaptor 패턴 (Wrapper)
 
 
 
이미 제공된 것과 필요한 것 사이의 간격을 매우는 디자인 패턴입니다.
Wrapper pettern 이라고도 합니다.
 
Adaptor 패턴은 기존의 클래스는 수정을 가하지 않고 목표로 한 인터페이스(API)에 맞추어
클래스를 만드는 방법입니다. 
기존의 버전과 새로운 버전에 대한 부작용을 최소화 해주면서 유지보수를 간단하게 하는데 도움이 되는 패턴입니다.

제공된 클래스 
 
Plug.java
/** * 이미 제공되어 있는 클래스 */
public class Plug {
private String specification = "5 AMP";
public String myPlug() {
return specification;
}
}// End of class
 
필요한 클래스
 
Socket.java
public interface Socket {
/** * 필요한 인터페이스 */
public String needSocket();
}// End of interface
 
Adaptor 클래스
 
ConnectorAdapter.java
public class ConnectorAdapter implements Socket {
/** * Socket.java 와 Plug.java 를 연결해주는 Adaptor 역할을 한다. */
public String needSocket() {
Plug plug = new Plug();
String output = plug.myPlug();
return output;
}
}// End of class
 


 
<그림 1 UML 다이어그램>

[출처] adaptor 패턴|작성자 달콤인생

 

 

복사

http://blog.naver.com/han1448oppa/40026386537

 

반응형

http://www.devbb.net/viewtopic.php?f=36&t=1086


Command 패턴


의도요청을 객체로 캡슐화함으로써 서로 다른 요청으로 클라이언트를 파라미터화하고, 요청을 저장하거나 기록을 남겨서 오퍼레이션의 취소도 가능하게 한다.

요청받은 오퍼레이션이 무엇이며, 이를 처리할 객체가 누구인지에 대한 아무런 정보 없이도 임의의 객체에게 메시지를 보내야 할 때가 있다.

Command 패턴은 메시지 자체를 객체로 처리함으로써 명시되지 않은 애플리케이션 객체의 요청을 처리할 수 있도록 지원한다. 메시지 객체는 다른 객체처럼 저장될 수 있고 전달될 수도 있다.


활용성
  • 수행할 행위 자체를 객체로 파라미터화하고자 할 때 Command 패턴을 사용한다. 절차지향 프로그램에서는 이를 콜백(callback) 함수로 사용했었다. 즉 어딘가에 등록해 두었다가 나중에 호출되도록 한 콜백 함수와 동일한 방식이다.
  • 서로 다른 시간에 요청을 명시하고, 저장하고, 수행할 수 있다. Command 객체는 원래의 요청과는 다른 생명 주기를 갖는다. 요청을 받아서 처리하는 객체는 호출될 함수의 주소 지정 방식과는 독립적으로 정의되어 있기 때문에 Command 객체를 다른 프로세스에 넘겨주고 해당하는 처리를 하게 할 수 있다.
  • 명령어 객체 자체에 실행 취소에 필요한 상태를 저장할 수 있다.
  • 변경 과정에 대한 로그를 남겨 두면 시스템이 고장났을 때 원상태로 복구가 가능하다.
  • 기본적인 오퍼레이션의 조합으로 좀더 고난이도의 오퍼레이션을 구성할 수 있다. 정보 시스템의 알반적인 특성은 트랜잭션을 처리해야 한다는 것이다. 트랜잭션은 일련의 과정을 통해 데이터를 변경하는 것인데, Command 패턴은 이런 트랜잭션의 모델링을 가능하게 한다. Command 클래스는 일관된 인터페이스를 정의하고 있는데, 이로써 모든 트랜잭션이 동일한 방식으로 호출된다. 새로운 트랜잭션을 만들면 상속에 의해 Commnad 클래스를 확장하면 되므로 시스템의 확장도 어렵지 않다.


구조
Attachment:
command.PNG


참여객체
  • Command : 오퍼레이션 수행에 필요한 인터페이스 선언
  • ConcreteCommand : Receiver 객체와 액션 간의 연결성을 정의한다. 또한 처리 객체에 정의된 오퍼레이션을 호출하도록 Execute를 구현한다.
  • Client : ConcreteCommand 객체를 생성하고 처리 객체로 정의한다.
  • Invoker : 명령어에게 처리를 수행할 것을 요청한다.
  • Receiver : 요청에 관련된 오퍼레이션 수행 방법을 알고 있다.

협력방법
  • 클라이언트는 ConcreteCommand 객체를 생성하고 이를 처리 객체로 지정한다.
  • Invoker 클래스는 ConcreteCommand 객체를 저장한다.
  • Invoker 클래스는 command 에 정의된 Execute() 함수를 호출한다. 명령어가 취소 가능한 것이라면 ConcreteCommand 는 이전에 Execute() 호출 전 상태의 취소 처리를 위해 저장한다.
  • ConcreteCommand 객체는 요청을 실제 처리할 객체에 정의된 오퍼레이션을 호출한다.


구현ConcreteCommand 서브클래스들은 수신 객체에 대한 참조자를 인스턴스 변수로 저장하고 있으며, 
이 수신 객체 정의된 메시지를 호출하도록 Execute() 를 구현함으로써 수신자와 처리 행위 간의 쌍을 정의한다. 
수신 객체는 자신에게 전달된 요청을 어떻게 처리해야 하는지를 알고 있다.
명령어가 지금까지 수행해 온 것을 뒤집는 오퍼레이션을 제공한다면 실행과 취소를 지원할 수 있다. 
이때 취소 프로세스 내에 오류가 누적되는 것은 피해야 한다.


예제코드Command 추상 클래스
Code:
class Command {
public:
   virtual ~Command();
   virtual void Execute() = 0;
protected:
   Command();
};

OpenCommand 클래스 : 선택한 이름의 문서를 여는 처리를 추상화한 객체.
Code:
class OpenCommand : public Command {
public:
   OpenCommand(Application* a)
   {
      _application = a;
   }
   virtual void Execute()
   {
      const char* name = AskUser();
      if( name != 0 ) {
         Document* doc = new Document(name);
         _application->Add(doc);
         doc->Open();
      }
   }
protected:
   virtual const char* AskUser();
private:
   Application* _application;
   char* _response;
};

PasteCommand 클래스 : 붙여 넣기 처리를 추상화한 객체
Code:
class PasteCommand : public Command {
public:
   PasteCommand(Document* doc)
   {
      _document = doc;
   }
   virtual void Execute()
   {
      _document->Paste();
   }
private:
   Document* _document;
};

명령어 취소나 특별한 아규먼트를 요청하지 않는 단순한 명령어라면 명령어의 처리 객체 자체를 파라미터로 하는 템플릿 클래스를 정의할 수 있다.
SimpleCommand 클래스는 Command의 서브클래스로, 처리 객체에 해당하는 Receiver 타입으로 파라미터화하여 처리 객체와 처리할 작업 간의 연결 관계를 관리한다.
Code:
template<class Receiver>
class SimpleCommand : public Command {
public:
   typedef void(Receiver::* Action)();
   SimpleCommand(Receiver* r, Action a) : _reciever(r), _action(a) { }
   virtual void Execute()
   {
      (_receiver->*action)();
   }
};

하나의 처리만을 다루는 것이 아니라 여러 가지 처리를 일련의 순서대로 수행해야 할 경우를 생략해서 MacroCommand 클래스를 정의한다.
MacroCommand 클래스는 일련의 명령어들을 처리하는 것으로서 일련의 명령어를 추가하거나 삭제하는 역할도 담당한다.
Code:
class MacroCommand : public Command {
public:
   MacroCommand();
   virtual ~MacroCommand();
   virtual void Add(Command* c)
   {
      _cmds->Append(c);
   }
   virtual void Remove(Command* c)
   {
      _cmds->Remove(c);
   }
   virtual void Execute()
   {
      for( auto it = _cmds.begin(); it != _cmds.end(); ++it ) {
         Command* c = (*it);
         c->Execute();
      }
   }
private:
   list<Command*> _cmds;
};


관련패턴MacroCommand 를 구현하고자 할 때 우리는 Composite 패턴을 사용하였다. 취소를 처리할 때 객체의 상태를 관리하고자 한다면 Memento를 사용해야 한다. 또 명령어가 처리되어 처리된 이력 리스트에 저장되기 전에 명령어를 복사해야 한다면 Prototype 패턴을 사용하면 된다.


_________________
외길을 가는 사람의 뒷모습은 아름답다.
 

 

 

반응형

http://kiro86.egloos.com/664679

게임 오브젝트 설계 #4 - 메시지 통신(messaging) 아키텍처 이야기

컴포넌트 기반 게임 오브젝트에 메시지 통신을 사용하는 방법은

컴포넌트끼리 의존하는 경우가 많아지면서 생기는 문제들을 보완하고자 제안된 방법입니다.

Game Programming Gems 6권에 나온 '게임 객체 구성요소 시스템'에서는 

메시지 통신을 하지 않고 컴포넌트끼리 직접 접근하는 방식으로 컴포넌트 기반 설계가 소개되었는데요.

GDC, KGC, NDC 등 개발 컨퍼런스에서 컴포넌트 기반 게임 오브젝트로 발표된 

대부분의 자료에서 메시지 통신이 언급되는 것을 보면 보편적으로 사용되는 방법이라고 볼 수 있을 것 같습니다.


메시지 통신을 설명하기 전에 먼저 예시를 하나 만들어봅시다.

게임 오브젝트가 A와 B라는 컴포넌트를 가지고 있다고 가정하고,

A는 B에게 종이를 가져와서 종이학을 접는 일을 한다고 생각해 봅시다.

메시지 통신을 하지 않는 컴포넌트에서는 A가 B에게 직접 종이를 달라고 말하고 

그렇게 종이를 받아와서 종이학을 접는 형태가 될 것입니다.

따라서 B가 없어지고 종이를 줄 수 있는 컴포넌트가 C로 변경된다거나,

또는 B에게 종이를 받기 위한 인터페이스가 달라진다면

B를 사용하고 있던 A도 역시 수정을 해야 합니다.

이것이 문제점으로 지적되었던 컴포넌트끼리의 의존성입니다.


그렇다면 메시지 통신을 하면 어떻게 될까요?

메시지는 편지나 쪽지와 같다고 생각하면 이해하기가 편합니다.

직접 가서 "종이를 내놓으시지 B군" 이라고 말하는 것이 아니라

"나 종이 필요해" 라고 쪽지를 남겨놓는 것입니다.

그리고 그 쪽지를 본 컴포넌트들 중에 종이를 줄 수 있는 컴포넌트가 있다면

그 컴포넌트가 A에게 종이를 주는 것이죠.

이렇게 만들면 A는 종이가 있으면 종이학을 접고 종이가 없으면 쪽지를 남겨놓고 기다리면 되는것입니다.

종이를 줄 수 있는 B가 없어지고 C가 줄 수 있게 되거나, B의 인터페이스가 변경되는것은

A에게 아무런 영향을 주지 않습니다. 이렇게 의존성이 사라진것입니다.

class GameObject
{
    ...

    void OnMessageMessage message )
    {
        컴포넌트들 돌면서
        {
            컴포넌트->OnMessage( message );
        }
    }
};

 

class ComponentBase
{
...

virtual void OnMessageMessage message ) = 0;
};

위 코드가 메시지 통신을 하는 컴포넌트의 기본적인 형태입니다.

컴포넌트들을 알고 있는 게임 오브젝트 또는 메시지 통신을 중개하는 특정 객체에 메시지를 보내면

메시지를 받아야 할 컴포넌트들을 돌면서 각 컴포넌트들에게 메시지를 전달하고

컴포넌트들은 메시지를 받게 되면 자신이 처리할 수 있는 메시지인지 확인 후에

해당 메시지를 처리하는 형태입니다.

class ComponentB
{
    ...

    virtual void OnMessageMessage message )
    {
        switch( message.GetType() )
        {
            case eGOCMessage::나에게 종이를 달라:
                {
                    종이를 달라고 한 컴포넌트에게 종이를 준다.
                }
                break;

            case eGOCMessage::나에게 풀을 달라:
                {
                    풀을 달라고 한 컴포넌트에게 풀을 준다.
                }
                break;
        }
    }
};

이런 식으로 B에서 처리할 메시지인지를 판단하고 처리해야 하는 메시지이면

그에 맞는 처리를 해주는 형태입니다.

이렇게 하면 컴포넌트들끼리 직접 접근을 하지 않고도 서로 메시지를 주고 받으며 복잡한 처리도 가능하게 됩니다.

그렇기 때문에 어떤 컴포넌트의 인터페이스가 변경되거나 삭제되거나 하더라도 

해당 컴포넌트를 사용하던 모든 코드를 뒤져서 수정해야 하는 문제는 없어집니다.

따라서 컴포넌트의 추가와 삭제가 수월해져 컴포넌트 기반 설계의 장점이 더욱 빛나게 되는 것이지요.


하지만 메시지 통신에도 문제는 있습니다.

컴포넌트에서 메시지를 받았을 때 메시지의 타입을 확인하는 과정은 어떻게 해도 없앨 수 없기 때문에

switch case의 폭발은 언제 봐도 유쾌하지 않은 문제입니다.

그리고 더 중요한 문제는 복잡한 일을 처리하기 위해 여러 개의 컴포넌트가 같이 통신을 해야 하는 경우

컴포넌트끼리 서로를 모르기 때문에 어떤 컴포넌트에서 어떤 일을 먼저 처리 해야 하는지 알 수 없어서

처리하는 일의 순서를 보장하기가 쉽지 않다는 것입니다.

 

반응형

http://kiro86.egloos.com/663439

 

게임 오브젝트 설계 #3 - 컴포넌트 기반 설계(component based) 

 

 

게임 오브젝트의 컴포넌트 기반 설계는 계층 구조 설계의 문제점들을 보완하고자 제안된 설계 방법입니다.

컴포넌트 시스템을 게임 오브젝트에 적용하는 아이디어는 GDC2002에서 Scot Bilas에 의해 발표된

A Data Driven Game Object System에서 소개되었으며, 보편적으로 사용되기 시작한 때는

2006년에 출판된 Game Programming Gems 6권에 '게임 객체 구성요소 시스템'으로 소개된 이후부터인 것 같습니다.

구글을 뒤져봐도 component based game object에 대한 내용은 2006년 이후 자료가 대부분이기도 하고요..


컴포넌트 기반 설계는 게임 오브젝트가 해야 할 기능들을 각각 별도의 객체로 생성하여 

게임 오브젝트의 클래스 폭발과 게임 오브젝트의 비대화 등의 문제점들을 해결하는 설계입니다.

 




컴포넌트 기반 설계에서 게임 오브젝트는 각 타입에 따라 사용되는 컴포넌트들로 구성됩니다.

오브젝트마다 필요한 기능이 있다면 그 기능을 하나의 컴포넌트로 만들고 

컴포넌트들을 추가하여 하나의 게임 오브젝트가 만들어지는 것입니다. 

게임 오브젝트는 단지 컴포넌트들의 관리만 할 뿐이죠.

class ComponentBase;
class Entity
{
public:
    Entityentity_id entityID );
    ~Entity();

    void Update( float elapsedTime );

    entity_id GetEntityID() const;
    
    void SetPositionconst Vector3& position );
    const Vector3GetPosition() const;

    void SetOrientationconst Quaternion& orientation );
    const QuaternionGetOrientation() const;

    bool InsertComponent( ComponentBase* pComponent );
    ComponentBase* GetComponentconst component_id& componentID );
    const ComponentBase*GetComponentconst component_id& componentID )const;
    void ClearComponents();

private:
    entity_id m_entityID;

    Vector3 m_position;
    Quaternion m_orientation;

    typedef boost::unorderd_map<component_id, ComponentBase*> ComponentTable;
    ComponentTable m_components;
};


게임 오브젝트가 컴포넌트들을 관리 할 때는 컴포넌트마다 고유 식별자가 필요합니다.

그리고 이 식별자를 GetComponent 함수의 인자로 받아 해당 컴포넌트를 얻어올 수 있도록 합니다.



컴포넌트 기반 설계에서는 게임 오브젝트가 하는 거의 모든 일들이 컴포넌트에 분산되어있기 때문에

역시 컴포넌트가 가장 중요한 요소라고 할 수 있습니다.

컴포넌트는 자신이 담당한 기능을 처리하는 객체입니다.

'화면에 그려지는 기능', '생명치 관련 처리', 'AI', '물리 시뮬레이션', '피격에 대한 처리' 등등

따로 떼어낼 수 있는 모든 기능들이 컴포넌트로 만들어질 수 있습니다.

 

class Entity;
class ComponentBase
{
public
:
    ComponentBase
() : m_pOwner( NULL ) {}
    virtual ~ComponentBase
() = 0 {}

    virtual const component_id& GetComponentID() const 
= 0;
    virtual const component_id& GetFamilyID() const 
= 0;

    virtual void Updatefloat elapsedTime 
) {}

    void SetOwnerEntity* pOwner 
);
    GameObject* GetOwner() const;

private:
    GameObject
* m_pOwner;
};

 

 

 


컴포넌트들은 계층 구조로 설계할 수 있습니다. 게임 오브젝트를 계층구조로 만드는 것이 아닌

하나의 기능을 담당하는 컴포넌트를 계층 구조로 만들어 복잡도를 낮추고 효율적으로 만들 수 있게 된 것입니다.

이때 상속 관계에 있는 컴포넌트들은 서로 연관된 처리를 하는 것일테고, 이렇게 연관된 컴포넌트들은

게임 오브젝트가 두 개 이상 가질 필요가 없는 경우가 존재합니다.

이것을 분간하기 위해 '패밀리 식별자' 라는것이 존재합니다.
(패밀리 라는 이름은 GPG6-게임 구성요소 시스템 에 사용된 용어를 그대로 가져왔습니다.)

이 패밀리 식별자를 사용하여 게임 오브젝트에서 컴포넌트를 얻어 사용하는 방식입니다.



예를 들어 RenderComponent를 만들려고 합니다. RenderComponent는 화면에 그려지는 기능을 처리합니다.

화면에 그리기 위한 모델데이터를 가지고 있고, 그리기 위해 Render 함수를 제공하거나

또는 외부에서 Render를 하기 위해 모델 데이터를 반환하는 함수가 필요하겠죠.


이제 이 컴포넌트를 게임 오브젝트에 추가를 해주면 게임 오브젝트는 화면에 그려질 수 있습니다.

Entity* pEntity = new Entity();
RenderComponent* pRenderComponent = new RenderComponent();
pEntity->InsertComponentpRenderComponent );

...

ComponentBase* pComponent = pEntity->GetComponent"render" );
if( pComponent != NULL )
{

    RenderComponent* pRenderComponent = 
        static_cast<RenderComponent*>( pComponent );
    pRenderComponent->
Render();
}

그런데 만약 파일에서 읽은 모델을 그려야 하는 경우도 있고, 

프로그램 내부에서 어떠한 지오매트리 모델을 그려야 하는 등 RenderComponent를 나눠야 한다면..


이런 식으로 RenderComponent에 계층 구조를 만들어서 각자 타입에 맞게 기능을 구현할 수 있습니다.

그런데 이 경우에는 게임 오브젝트가 ModelRenderComponent와 GeometryRenderComponent를 모두 가질 수 있습니다.

같은 기능을 하는 컴포넌트이기 때문에 이런 경우는 로직이 복잡해지거나 이상해질 수 있습니다.

Entity* pEntity = new Entity();
ModelRenderComponent* pModelRenderComponent = new ModelRenderComponent();
pEntity->InsertComponent( pModelRenderComponent ); 
// ?
GeometryRenderComponent* pGeometryRenderComponent = new GeometryRenderComponent();
pEntity->InsertComponent( pGeometryRenderComponent ); // ?

...

ComponentBase* pComponent = pEntity->GetComponent( "model_render" );
if( pComponent != NULL )
{
    ModelRenderComponent* pModelRenderComponent =
        static_cast<ModelRenderComponent*>( pComponent );
    pModelRenderComponent->Render(); // ?
}

pComponent = pEntity->GetComponent( "geometry_render" );
if( pComponent != NULL )
{
    GeometryRenderComponent* pGeometryRenderComponent =
        static_cast<GeometryRenderComponent*>( pComponent );
    pGeometryRenderComponent->Render(); // ?
}

물론 의도하여 이런 코드를 작성 할 수도 있지만 보통의 경우에는 의도한 부분이 아닐 것입니다.

이런 문제를 해결하기 위해 같은 기능을 하는 컴포넌트들을 묶어서 패밀리 식별자를 이용하여 얻어오는 것입니다.

Entity* pEntity = new Entity();
ModelRenderComponent* pModelRenderComponent = new ModelRenderComponent();
pEntity->
InsertComponent( pModelRenderComponent );


...


ComponentBase* pComponent = pEntity->GetComponent( "render" );
if( pComponent != NULL)
{

    RenderComponent* pRenderComponent =
        static_cast<RenderComponent*>( pComponent );
    pRenderComponent->
Render();
}

이런 식으로 게임 오브젝트를 설계하는 것이 컴포넌트 기반 설계의 기본입니다.

이런 방식을 기본으로 하여 조금 더 추가된 것이 '메시지 통신'을 하는 컴포넌트 기반 게임 오브젝트 인데요

컴포넌트가 RenderComponent처럼 자기 혼자서 하나의 기능을 모두 처리 할 수 있으면 좋겠지만

아쉽게도 게임이 이렇게 간단한 기능들만 요구하지는 않습니다.

하나의 로직을 처리하기 위해 여러 개의 컴포넌트가 서로를 참조하여 서로에게 데이터를 얻어 실행되어야 하는

그러니까 컴포넌트끼리 의존하게 되는 기능들이 굉장히 많이 존재하게 됩니다.

예를 들어 AIComponent를 만든다고 해도 여러 가지 데이터들을 얻어와야 하기 때문에

다른 컴포넌트에 접근 할 수 밖에 없는데요.

이 기능에 대한 로직을 처리 할 때 컴포넌트끼리의 의존성을 줄이기 위해 제안된 방법이 메시지 통신입니다.

아무래도 컴포넌트에서 다른 컴포넌트에 직접 접근하여 함수 호출을 하는 식의 의존성이 많아지게 되면

어떠한 컴포넌트의 인터페이스를 수정하거나 삭제를 했을 경우 

그 컴포넌트를 사용하고 있는 모든 컴포넌트를 수정해 주어야 하는 문제가 생겨버리겠지요..

반응형

http://neovader.tistory.com/110


Visual Studio의 테스트 기능을 활용하여 단위 테스트를 만들어 보겠습니다.

 새 프로젝트를 선택합니다.



기타 프로젝트 형식 > Visual Studio 솔루션의 빈 솔루션을 선택하고 이름을 작성합니다.

새 프로젝트를 추가합니다.

Visual C# > Windows > 클래스 라이브러리 선택 후 이름을 입력하고 확인버튼을 클릭합니다.

솔루션 탐색기에서 Class1.cs 파일을 찾아서 Basics.cs로 파일명을 변경합니다.

Basics.cs를 다음과 같이 작성합니다.

namespace CoolMath
{
    public class Basics
    {
        public int Add(int i, int j)
        {
            return i + j;
        } 

        public int Subtract(int i, int j)
        {
            return i - j;
        } 

        public int Multiply(int i, int j)
        {
            return i * j;
        } 

        public int Divide(int i, int j)
        {
            return i / j;
        }
    }
}

그림과 같이 마우스 오른쪽 버튼 클릭 후 단위 테스트 만들기를 선택합니다.

단위 테스트 만들기 창이 나타나면, 체크박스를 그림과 같이 모두 체크하고 확인 버튼을 클릭하니다.

프로젝트 이름을 입력 후 만들기 버튼 클릭합니다.

솔루션 탐색기에 그림과 같이 테스트 프로젝트가 추가됩니다.

BasicsTest.cs를 다음과 같이 작성합니다.

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CoolMath
{
    [TestClass()]
    public class BasicsTest
    {
        [TestMethod()]
        public void Add_0_and_0_Expert_0()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(0, mathBasics.Add(0, 0));
        } 

        [TestMethod()]
        public void Add_1_and_1_Expert_2()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(2, mathBasics.Add(1, 1));
        } 

        [TestMethod()]
        public void Add_0_and_IntMaxValue_Expert_IntMaxValue()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(int.MaxValue, mathBasics.Add(0, int.MaxValue));
        } 

        [TestMethod()]
        public void subtract_1_from_2_Expert_1()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(1, mathBasics.Subtract(2, 1));
        } 

        [TestMethod()]
        public void Subtract_0_from_IntMaxValue_Expert_IntMaxValue()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(int.MaxValue, mathBasics.Subtract(int.MaxValue, 0));
        } 

        [TestMethod()]
        public void Multiply_11_and_11_Expert_121()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(121, mathBasics.Multiply(11, 11));
        } 

        [TestMethod()]
        public void Multiply_7_and_0_Expert_0()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(0, mathBasics.Multiply(7, 0));
        } 

        [TestMethod()]
        public void Divide_121_by_11_Expert_11()
        {
            Basics mathBasics = new Basics();
            Assert.AreEqual(11, mathBasics.Divide(121, 11));
        } 

        [TestMethod()]
        [ExpectedException(typeof(System.ApplicationException))]
        public void Divide_7_by_0_Expect_Exception()
        {
            Basics mathBasics = new Basics();
            mathBasics.Divide(7, 0);
        }
    }
}

그림과 같이 테스트 뷰를 선택합니다.

그림과 같이 테스트 뷰가 표시됩니다. 모두 선택 후 실행 버튼을 클릭합니다.

테스트 결과 창에 결과가 표시됩니다.

1건의 테스트가 실패되었기 때문에 결과는 테스트 실행 실패이며, 실패한 항목의 오류 메시지도 표시됩니다. 실패한 항목을 더블 클릭하면 자세한 내용을 볼 수 있습니다.

12


오류 메시지는 테스트 메소드가 System.ApplicationException을 예상했지만, System.DivideByZeroException이 throw됐다는 내용입니다.

오류 스택 추적에서 테스트 메소드와 실제 메소드의 오류가 발생한 곳이 표시되며, 따라가서, 소스를 수정할 수 있습니다.

테스트 메소드를 수정하여 System.ApplicationException으로 예상한 것을 System.DivideByZeroException으로 예상하도록 변경할 수 있습니다.

[TestMethod()]
        [ExpectedException(typeof(System.DivideByZeroException))]
        public void Divide_7_by_0_Expect_Exception()
        {
            Basics mathBasics = new Basics();
            mathBasics.Divide(7, 0);
        }

아니면실제 메소드를 수정하여 테스트 수행 결과의 예상 값에 맞도록 수정할 수도 있습니다.

public int Divide(int i, int j)
        {
            if (j == 0)
            {
                throw new System.ApplicationException("0으로 나눌  없습니다.");
            }
            return i / j;
        } 

둘 중 하나를 수정하여 다시 빌드 후, 테스트를 수행하면 다음과 같이 통과가 됨을 알 수 있습니다.

이것은 간단한 예였지만이러한 테스트 코드가 지속적으로 관리되고소프트웨어가 계속적으로 변경된다면수정  기존 테스트 코드를 활용하여 회귀 테스트를 자동화   있습니다그렇게 된다면소스 수정으로 인한 예상하지 못했던 오류를 미연에 방지할 있습니다.

반응형

 

 

 

 

예전에 캐스팅하던 방법은 아래와 같다

 

 

//상속관계에 있는 두 클래스간의 상대적 거리값을 계산한다

//1 을 각 클래스* 로 캐스팅하여 빼면 두 클래스간의 포인터간 거리간격을 알 수 있고

//뺄셈으로 offset 을 알아낼 수 있다, 이것을 아래 thisPtr 에 더하면 thisPtr 에서부터 상대적으로

//offset만큼 떨어진곳(즉 자식)의 포인터를 알아낼 수 있게된다

int offset = (int)(T*)1 - (int)(Singleton_sjh<T>*)(T*)1;

 

//offset 이 int 이기때문에 _thisPtr 을 int 로 캐스팅해 더해줘야한다

//한 클래스에 여러개 클래스가 상속되면서 그때 싱글톤도 같이 상속될 경우 상속되는 것과 자식중 자식의

//주소로 싱글톤 포인터를 잡아줘야한다, 여러개 상속하면 주소가 자식의 주소가 아닌 다른 부모의 주소가 됨으로

//그냥 thisPtr 을 대입하면 Singleton_sjh 의 주소로 들어감으로 offset 을 더해 자식(T)의 주소 변환해줘야한다

 

_singleton=((T*)((int)_thisPtr + offset));

 

 

 

 

 

 

class aaa 

public:

int a;

aaa() : a(1)

{

}

 

void show()

{

std::cout<<"aaa"<<std::endl;

}

};

 

 

 

class ccc : public aaa, public Singleton_sjh<ccc>{

public:

int c;

ccc() : c(3)

{

  }

 

void show()

{

std::cout<<"ccc"<<std::endl;

}

};

 

int main(void)  

{  

//offset 을 강제로 0 을 대입하면  이레 show 함수 호출에서 오류가 난다는것을 알 수 있다

ccc::createInstanceWithAutoRelease();

ccc::GetinstanceSP()->show();

 

return 0;  

}  

 

 

 

 

 

 

하지만 현재는 static_cast 만으로도 캐스팅이 안전하게 된다

 

 

 

 

[주의해야하는 사항]

 

싱글톤을 상속받는 자식클래스는 싱글톤에서 자식을 템플릿 T 로 부르기때문에 자식의 생성자또한 헤더에 생성자가

기술되어있어야한다, 싱글톤이 템플릿임으로

그렇지 않을경우 얘기치 않은 메모리릭이나 오류가 발생할 수 있다, 스마트포인터로 싱글톤을 작성할때는 더더욱 이것을 지켜야한다

 

 

 


http://www.gpgstudy.com/forum/viewtopic.php?t=23728
 
 

해당 글의 원래 필자인 Scott Bilas가 자기 웹사이트에 이것과 관련해서 추가적으로 설명한게 있습니다. 

http://www.drizzle.com/~scottb/publish/gpgems1_singleton_notes.htm 

읽어보시면 아시겠지만 결론적으로 자신이 이 코드를 쓸 당시 Visual C++ 5를 사용한거 같은데 그 때는 간단한 캐스팅으로는 코드가 동작하지 않았다고 합니다. 

그래서 컴파일된 코드를 가지고 디어셈블링해서 저런 코드를 만들어 냈다고 하는군요. 당연히 이식성은 생각안했다고 합니다 . 어쨌든 결론은 지금은 그냥 static_cast을 써서 간단히 캐스팅해도 잘 된다는 것 입니다. 

코드:
Singleton( void )
{
  assert( !ms_Singleton );
  ms_Singleton = static_cast <T> (this);
}



이 답변은 GameDev에서 참고했습니다. 
http://www.gamedev.net/community/forums/topic.asp?topic_id=433262
_________________
Stay Hungry. Stay Foolish. 

 

 

반응형

 
UML 클래스 다이어그램: 참조

UML 클래스 다이어그램은 응용 프로그램에서 내부적으로나 사용자와 통신하여 사용하는 개체 및 정보 구조를 기술합니다. 여기에서는 특정 구현에 대한 참조 없이 정보를 기술합니다. 또한 데이터베이스 테이블, XML 노드 또는 소프트웨어 개체 컴퍼지션과 같은 다양한 방법으로 클래스 및 관계를 구현할 수 있습니다.

참고참고

이 항목에서는 UML 클래스 다이어그램에 대해 설명합니다. 이 외에도 프로그램 코드를 시각화하는 데 사용되는 .NET 클래스 다이어그램이 있습니다. 자세한 내용은 클래스와 형식 디자인 및 보기를 참조하십시오.

클래스 다이어그램 읽기

이 단원의 표에서는 UML 클래스 다이어그램에서 볼 수 있는 요소에 대해 설명합니다. 이러한 요소의 속성에 대한 자세한 내용은 다음 항목을 참조하십시오.

UML 클래스 다이어그램을 그리는 방법에 대한 자세한 내용은 UML 클래스 다이어그램: 지침을 참조하십시오. UML 클래스 다이어그램을 만들려면 아키텍처 메뉴에서 새 다이어그램을 클릭합니다. 모델링 다이어그램을 만들고 그리는 방법에 대한 자세한 내용은 방법: UML 모델 및 다이어그램 편집를 참조하십시오.

관계와 속성을 보여 주는 세 개의 클래스

모양

요소

설명

1

클래스

지정된 구조 또는 동작 특징을 공유하는 개체의 정의입니다. 자세한 내용은 UML 클래스 다이어그램 형식의 속성을 참조하십시오.

1

분류자

클래스, 인터페이스 또는 열거형의 일반 이름입니다. 구성 요소, 사용 사례 및 행위자도 분류자입니다.

2

축소/확장 컨트롤

분류자의 세부 정보를 볼 수 없으면 해당 분류자의 왼쪽 위에 있는 확장기를 클릭합니다. 각 세그먼트에서 [+]를 클릭해야 할 수도 있습니다.

3

특성

분류자의 각 인스턴스에 연결된 형식화된 값입니다.

특성을 추가하려면 특성 섹션을 클릭하고 Enter 키를 누릅니다. 그런 다음 특성의 시그니처를 입력합니다. 자세한 내용은 UML 클래스 다이어그램 특성의 속성을 참조하십시오.

4

작업

분류자 인스턴스에서 수행할 수 있는 메서드 또는 함수입니다. 작업을 추가하려면 작업 섹션을 클릭하고 Enter 키를 누릅니다. 그런 다음 작업의 시그니처를 입력합니다. 자세한 내용은 UML 클래스 다이어그램 작업의 속성을 참조하십시오.

5

연결

두 분류자의 멤버 간 관계입니다. 자세한 내용은 UML 클래스 다이어그램 연결의 속성을 참조하십시오.

5a

집계

공유 소유권 관계를 나타내는 연결입니다. 소유자 역할의 집합체 속성은 공유로 설정됩니다.

5b

컴퍼지션

전체와 부분 관계를 나타내는 연결입니다. 소유자 역할의 집합체 속성은 복합으로 설정됩니다.

6

연결 이름

연결 이름입니다. 이 이름은 비워 둘 수 있습니다.

7

역할 이름

연결의 한 쪽 끝에 있는 역할의 이름입니다. 이 이름은 연결된 개체를 참조하는 데 사용할 수 있습니다. 이전 그림에서 임의의 OrderO에 대해O.ChosenMenu는 연결된 Menu입니다.

각 역할에는 고유한 속성이 있으며, 이러한 속성은 연결 속성 아래에 나열됩니다.

8

복합성

이 쪽 끝의 개체 중에서 다른 쪽의 각 개체에 연결할 수 있는 개체의 수를 나타냅니다. 이 예제에서 각 Order는 정확히 하나의 Menu에만 연결되어야 합니다.

*는 연결할 수 있는 링크 수에 상한이 없음을 의미합니다.

9

일반화

특정 분류자는 일반 분류자에서 정의의 일부를 상속합니다. 일반 분류자는 연결선의 화살표 끝에 있습니다. 특성, 연결 및 작업은 특정 분류자에서 상속합니다.

두 분류자 간의 일반화 관계를 만들려면 상속 도구를 사용합니다.

인터페이스와 열거형을 포함하는 패키지

모양

요소

설명

10

인터페이스

외부에서 볼 수 있는 개체 동작의 일부에 대한 정의입니다. 자세한 내용은 UML 클래스 다이어그램 형식의 속성을 참조하십시오.

11

열거형

리터럴 값 집합으로 구성되는 분류자입니다.

12

패키지

분류자, 연결, 동작, 수명선, 구성 요소 및 패키지 그룹입니다. 논리 클래스 다이어그램은 멤버 분류자와 패키지가 패키지 내에 포함되어 있음을 나타냅니다.

Package1 내의 Class1이 패키지 외부의 Class1과 구별되도록 이름은 패키지 내에서 범위가 지정됩니다. 패키지 이름은 콘텐츠의 정규화된 이름 속성의 일부로 나타납니다.

UML 다이어그램의 연결된 패키지 속성을 설정하여 패키지를 참조할 수 있습니다. 그러면 해당 다이어그램에서 만드는 모든 요소가 패키지의 일부가 됩니다. 이러한 요소는 UML 모델 탐색기에서 패키지 아래에 나타납니다.

13

가져오기

한 패키지가 다른 패키지의 모든 정의를 포함한다는 것을 나타내는 패키지 간 관계입니다.

14

종속성

화살촉 끝에 있는 분류자가 변경되면 종속 분류자의 정의 또는 구현이 변경될 수 있습니다.

연결선과 롤리팝으로 표시된 구현

모양

요소

설명

15

인식

클래스는 인터페이스에서 정의하는 작업 및 특성을 구현합니다.

클래스와 인터페이스 간의 인식 관계를 만들려면 상속 도구를 사용합니다.

16

인식

같은 관계의 대체 표현입니다. 롤리팝 기호의 레이블은 인터페이스를 식별합니다.

이 표현을 만들려면 기존 인식 관계를 선택합니다. 그러면 연결 근처에 작업 태그가 나타납니다. 이 작업 태그를 클릭하고 롤리팝으로 표시를 클릭합니다.

반응형

http://k.daum.net/qna/view.html?qid=3ODJc

 

- 아키텍처: 소프트웨어의 주요 설계 구조
 
소프트웨어의 주요 특징들을 결정짓는 주요 설계 구조이다.즉, 소프트웨어의 주요 구성 요소 및 구성, 이들간의 
주요 인터페이스, 중요 동작 방식 등 소프트웨어의 주요 특징들을 결정짓는 모든 설계 구조를 포함한다.
소프트웨어의 주요 특징을 결정짓고 소프트웨어 개발에 미치는 영향도 매우 커서 소프트웨어 개발에 있어서 가장 중요한 
부분이라고 할 수 있다.지원 프로그램, 라이브러리, 언어, 다른 소프트웨어 구성 요소 등과 같이 구체적인 구현을 포함하지 않는다는 점에서 
프레임워크나 플랫폼과는 명확히 구분된다.
 
- 프레임워크: 소프트웨어 뼈대 구조
 
프레임워크는 다른 소프트웨어 프로젝트가 개발될 수 있는 뼈대 구조이다.지원 프로그램, 라이브러리, 언어, 
다른 소프트웨어 구성 요소들을 엮어 주는 소프트웨어 등을 포함하고 있다.따라서, 플랫폼도 프레임워크의 일종이라고 볼 수 있으며, 
MS사에서 닷넷 플랫폼을 닷넷 프레임워크라고 지칭하는 것도 틀린 것이 아니다.또한, UI 프로그램 개발을 위한 부분 만을 떼어내서 
프레임워크라고 할 수도 있다.UI 프로그램 개발을 위한 부분 만으로는 완전한 소프트웨어 실행 환경이 되지 않으므로 플랫폼은 아니지만 
프레임워크이다.이러한 점에서 프레임워크와 플랫폼은 다른 경우가 많다.
 
- 플랫폼: : 소프트웨어 실행 환경
 
가장 일반적이면서도 명료한 의미는 "소프트웨어가 실행되는 환경"이다.개발 언어나 개발 환경을 플랫폼에 
포함시키기도 하지만 이는 부수적 개념 혹은  확장된 개념에 불과하고, 핵심은 "소프트웨어가 실행되는 환경"이다.
각 프로그램은 아무 플랫폼에서나 실행되는 것이 아니고 특정 플랫폼에서만 실행된다.


반응형

블로그 이미지

3DMP engines

3D그래픽스 물리 수학, 프로그래밍 GPU Shader 게임엔진 알고리즘 디자인패턴 matlab etc..





디자인 패턴 : 특정 기술자들이 써온 기술들을 또는 자주 접했던 문제들에 대한 패턴(노하우)을 적어 놓은 것

                    문제에 대한 일반적인 방법을 기술해 놓은 것

 

비슷한 문제에 대한 품질이 올라 간다

 

사용보다는 왜 이걸 쓰는지가 먼저 이고 패턴의 목적이 무엇인지 생각해 봐야 한다

 

 

1. 디자인패턴의 용도 목적이 무엇인가가 관건이다

2. 목적으로 구분하여 해당 목적에 맞는 디자인패턴을 찾는다

3. 문제해결에 가장 적합한 방법에 맞는것을 선택

반응형


About 추상화 기초 클래스(abstract base class; ABC)  My Studying C++

복사http://blog.naver.com/bibliotheque/120016427399

   클래스 선언에 순수 가상 함수가 들어 있으면, 그 클래스의 객체를 생성할 수 없다. 그 이유는 순수 가상 함수를 가지고 있는 클래스는 기초 클래스의 역할을 하기 위해서만 존재하기 때문이다.

 

class BaseClass // 추상화 기초 클래스

{

private :

    // Member Variables..

 

public :

     BaseClass() {}

     virtual ~BaseClass() {}

 

     // other Member Methods..

     // ...

     virtual void SayHelloWorld() = 0; // 순수( = 0 표기) 가상(virtual 키워드) 함수

}

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

아키텍처/프레임 워크/플랫 폼에 관한 정의  (0) 2012.11.02
디자인 패턴 Why?, 그리고 목적  (0) 2012.11.02
Abstract Factory 패턴  (0) 2012.11.02
builder 패턴  (0) 2012.11.02
command 패턴  (0) 2012.11.02

 

 

 

 

여러환경에 대해 설계 된 상속 구조가 있고 해당 조건(환경) 에대한 조건이 주어지면

 

조건에 대한 객체의 new 를 리턴해주는 패턴

 

이로써 switch 분기문을 없앨 수 있다

 


 

 

다른 환경을 지원하기 위한 패턴

 

최악의 방법 : 따로따로 맞게 다 짠다

 

 

 

위에보다 나은 방법 :

 

일단 하나의 프로그램 내에서 짠다   :  switch , if 구문으로 분기 해서 처리 

 

but 변경 되는 부분마다 이부분을 처리

 

 

 

그렇다면 how?

 

분기 구문을 피하면서 변화된 부분을 따로 구성 해야 한다

또한 나중에 추가 되는 부분을 지원해 줄 수 있어야 한다( 확장성 )

 

 

부모 A 와 각 환경별의 자식 클래서 B,C,D....N 클래스를 구성해

 

상속 구조를 만들어 놓고 자식에서 해당 환경의 생성부분을 만들어주면

 

분기문을 피할 수 있고, 각각 독립적으로 구성 할 수 있다

 

 

 

 

 


 

 

 

 

 

 

- 프로그램 처음 세팅시 환경에 대해 조사해보고 해당 환경의 자식 클래스로 세팅한다

(해당 환경의 new 하여 부모에게 다운 캐스팅)

 

이때 E0, E1 이라는 환경이 존재 한다고 할때 각각의

 

E0 ,E1 에 대한 서브클래스를 만들어 놓고 생성 함수명을 동일하게 작성한다(virtual 키워드로)

 

필요한 객체 생성은 환경에 대한 Create 함수를 호출할때 해당환경의 자식 클래스에서 필요한 클래스들을 생성(new) 한다

이렇게 생성된 객체의 포인터를 반환해 처음 환경을 조사하는 부분으로 다시 되돌아오면 이 포인터들로 작업을 하면 된다

 

 

그렇게 한다면 동일한 이름으로 각 환경에 맞는 프로그램을 설계할 수 있다

 

하지만 E0, E1 의 두개일 경우 클래스가 2배가 된다, n 개가 되면 n 배가 된다

 

 

하지만 처음 생성할때는 분기문이 한번은 들어가야 한다, 이것을 피할 수 있는 방법이 있다면 좋겠지만

 

일반적으로 이부분은 힘들다고 보여진다

 

 

Abstract(추상) 이란 이름이 붙은 이유는 부모가 생성될 필요는 없고 그냥  virtual void MethodName()=0 순수가상함수를 담고 잇는

 

추상 클래스 로써 존재하기 때문

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

Abstract Factory  패턴은 Singleton 패턴과 혼용해 쓰일 수 있는데

 

각 환경에 대한 설정들이기 때문에 자식의 자식을 생성하는 경우는 많지 않다

 

대부분 부모 A 의 자식 1개 로 끝나는 상황이라면 Singleton 과 연계하여 사용

 

 

 

Abstract Factory 패턴은 Prototype 과 Factory method 패턴을 조합한 패턴으로 대체할 수 있다

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

디자인 패턴 Why?, 그리고 목적  (0) 2012.11.02
추상화 기초 클래스(abstract base class : ABC)  (0) 2012.11.02
builder 패턴  (0) 2012.11.02
command 패턴  (0) 2012.11.02
instance Pool 패턴  (0) 2012.11.02

 

 

 

 

빌더 패턴

 

추측 : 마치 프로그램 빌드 하는 것처럼 돌아간다고 해서 빌드 패턴인것 같다

 

Abstract Factory 패턴과 비슷하지만

 

Abstract Factory 는 해당 환경에 대한 클래스를 생성해서 바로 반환해 주지만

 

빌더 패턴은 자신의 해당구역에 저장해 놓으면서 조립하다가 함수를 통해서 완성된 것을

 

요청에 의한 반환에 의해 반환해 줄 수 있다는 것,

 

이때 Director 가 환경 또는 조건에 맞는 부분에 대해서 호출해준다

 

 

마치 건물이 단계적으로 만들어져서 만들어진 건물을 가지고 있는 것과 유사하다

 

 

 


 

 

 

 

 

txt 파일을 다른 파일로 변경할때

 

동일한 한개의 알고리즘 구문에 여러개의 변환 타입에 대해 코딩하면 변환 되는 부분이 있을때마다

 

switch 또는 if 분기 구문으로 그때마다 처리해 줘야 한다

 

이를 극복한 패턴이 빌드 패턴

 

 

상속구조로 각 파일 변환 타입을 컨테이너에 가지고 있고

 

 

해당 변환에 대한 분기구문이 있는 클래스 Director 클래스를 하나 만들어서

 

상속구조의 부모 클래스에 변환 요청을 하다 (함수 콜)

 

그럼 해당 변환 자식 클래스만 돌아가 변환 처리를 다른 변환(자식) 과 독립적으로 돌아 갈 수 있음으로 효율적이다

 

변환이 끝난 후 해당 자식에서 결과를 Director 에게 리턴해주는 등으로 처리해주면 된다

 

 

- 일반적으로 사용자는 이런 빌드 내부로직을 알 필요가 없게끔 하는 것이 좋다, 이건 용도나 개인적인 판단으로 구현을 선택.

 

 

반응형

 

 

 

 

 

command 패턴  (명령패턴)

 

하나의 함수 명령이 각 클래스에서 다른 정의로 실행된다

 

부모 A

 

자식 : B , C, D ,E

 

 

등등이 있을때 오버라이딩 구조로 동일한 함수로 각 클래스에서는 다른 동작을 한다

 

각 생성된 인스턴스들을 컨테이너에 저장되는게 일반적이다

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Abstract Factory 패턴  (0) 2012.11.02
builder 패턴  (0) 2012.11.02
instance Pool 패턴  (0) 2012.11.02
Singleton 패턴 , n 개 까지만 생성하고 싶다  (0) 2012.11.02
Prototype 패턴  (0) 2012.11.02

 

 

 

 

 

메모리 또는 객체들을 미리 생성해 놓고

 

요청시 생성된 메모리 or 객체등을 넘겨주고 사용이 끝나면 반환하는 패턴

반응형

 

 

 

싱글톤은 한번만 생성해서 사용 할 수 있지만

 

n 개 까지도 싱글톤 내부에서 생성 할 수 있다

 

(n 개는 한번씩만 생성됨)


 

 

 

 

[프로그램 전체에 Only one 객체만 존재 ]

 

전역변수로 글로벌을 생성할 경우 2,3,4,,....., n 번 생성될 가능성이 있다 이를 방지하기 위한 패턴

 

 오직 하나의 객체만 만들어 사용 한다라는 전제로 탄생한 패턴

 

point : 얻어오는 함수 와 파괴함수만 public 나머지는 private or protected

 

          생성자도 외부접근을 막아버려 생성을 못하게 하고 얻어오는 함수 에서 생성 처리를 해준다

 

 

 

정리 - static 키우워드로 전역적인 방법으로 객체를 한번 만들거나 혹은 만들어진 객체의 인스턴스 주소를 리턴해 주는 패턴

          그렇기에 객체를 직접 생성하는 생성자는 private  되어 있다

 

 

싱글톤 패턴은 메모리 풀 같은 객체 하나만 존재해야 할 가능성이 높은 클래스에 대해 쓰여지는 패턴이다

 

메모리풀을 사용자가 의미없이 여러개 생성하는 상황이 발생하면 메모리 부족으로 연결 될 수 있음

 

 


 

 

싱글톤으로 설정될 클래스가 부모=A, 자식=(B,C,D,E.....) 구조로

 

되어 있는 경우 자식의 개수가 정해져 있는 불변의 상태에서 사용하는 것이 가능하다

 

상태값을 외부에서 설명할 수 있게 해놓고(ex #define ) 필요한 subclass 에 대한 포인터를 넘겨 받는 것인데

 

만약 서브 클래스가 나주에 추가 된다면 싱글톤의 생성부분 함수를 수정해야 하는 상황이 발생(싱글톤은 수정하지 않는다는 원칙이 위배됨)

 

-> template 을 점목시키면 보다 나은 해결책이 될 수 있음

 

 


 

 

 

Tip : 멤버변수, 멤버 함수는 Static 으로 선언 because class 내부에서 static 끼리 접근가능 함으로

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

주의 :

 

1. 생성자를 만들어 놓지 않으면 묵시적 기본 생성자는 public 이므로 singleton 객체를 여러개 생성할 수 있는 상황이 벌어짐으로

 생성자를 private or protecte 에 선언해야 한다

 

 

2. 싱글톤의 복사 생성자도 private, protected 로 막아야 한다

복사 생성자가 불리는 경우 복사가 일어남으로 객체가 n 가 존재할 가능성이 있다( 싱글톤 철학에 위배 )

 

Singleton test( *Singleton::Getinstance() );

 
 
반응형

 

 

 

 

 

프로토타입 패턴

Prototype(원형)

 

한번복사되어 가져온 객체 A 가 있다고 가정

A 에는 A.a[10], A.b[10] , A.c[10]

 

각 변수가 있고 각 변수는 어떤 데이터들을 또 담을 수 있는 클래스라고 할때

 

이 클래스로 다양한 표현을 할 수 있는데 각 다양한 표현을 하기 위해서 객체복사는

 

한번만 이루어지고 복사되어진 것들을 복사된 것으로 계속 활용 한다는 패턴

 

 

그냥 일반적인 개념이다

 

서버 & 클라이언트에서는 부하를 줄이기 위한 필수적 개념

 


 

 

자신 복사 구문은 이렇게 처리한다

 

Clone() : 자신 복사

 

class A{
 int test;
public:
 A* Clone(){ return new A(*this); }
};

 

이런 Prototype 패턴의 이점중 하나는

 

 

1. but Abstract 패턴을 쓰지 않고 그냥 자기 자신만 복사할때 Clone() 사용

 

 

2.

만약 파서에서 이미 오브젝트 아이디로 구분되어져 있는 오브젝트들을 램으로 복사하기 우해 로드 할때

  

각 오브젝트들에 대한 타입을 분기문으로 구분해서 복사 차리 하지 않고

 

한줄로 처리가 가능한데 이땐 Abstract 패턴의 구조로(virtual) 오브젝트가 만들어지고

 

각 자식 클래스에서 Clone() 을 부르면 분기문 없이 바로 복사 처리 가능

 

이때 복사되는 전체 노드를 Composite 패턴과 연결하면 효과적

 

이때 Composite 패턴은 기존에 등록된 노드(객체) 들의 결ㅇ합 집합으로 이 자체를 복사하고자 할때

 

또는 Composite 와 Composite 끼리 묶어 다시 Composite 을 구성할때 유용

 

 

 

 

 


 

 

 

 

 

전체 복사 composite 도  Clone() 을 수행할때 위 처럼 유사하게 수행한다

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

신경써야 할 부분

 

Clone() 복사시 자기 자신의 Deep Copy 가 필요한가 살펴봐야함

 

노드들이 포인터 일경우 포인터를 잃어버리지 않도록 주의

 

 

- 순환 구조일경우 구현이 까다롭다( because of 서로서로 복사  )

반응형

 

 

 

 

[간단한 경우]

 

instance 를 만들기 위해

 

'A부모'의 생성자의 인수대로 작성해야 하는 상황이 발생 할 수 있는데

 

다른 곳에서도 A 클래스를 만들려고 할때 불필요하게 생성자 인수를 작성해야 하니 불필요한 일이 생길 수 있어서

 

class A{

 

public :

A Createinstance(){

 return A( "내용", 1234, 3 );

}

 

}

로써 인스턴스를 얻는 방법

 

Factory Method  : 공장처럼 인스턴스 찍어주는 함수

 

 

 

[사용범위를 좀더 확장한경우]

객체를 생성할때 FactoryMethod() 함수로 객체를 생성 할 경우

 

class A{

FactoryMethod()

 

class B : public A{

FactoryMethod()

...

 

 

객체 생성을 서브클래스에서 정의하는 경우에 사용 할 수 있는데

 

 

이렇게 하는 이유는 서브 클래스들에서 어떤 클래스로 생성이 결정 된다면

 

해당 클래스를 리턴해주긴 하지만 즉 다양한 클래스인스턴스를 넘겨줄 순 있지만

 

인스턴스를 만드는 명령키워드는 하나라는 것

 

 

 

하지만 이때 FactoryMethod 에 분류하기 위한 인수를 넘기는건 좋지 못한데

 

왜냐하면 그 분류키워드가 생성에서 그 분류키로 해당 클래스를 사용하는데 다른

 

곳에서도 분류가 되어야 하는 상황이 오면 그 키를 넘겨줘야 하기 때문

 

그래서 이때 하위 클래스와 부모 클래스는 abstract 패턴으로 만들어 확장이 용이 하고 구분 될 수 있도록 구성 하는 것이 좋다

 

 

 

[좀 더 사용범위를 좀더 확장한경우]

 

ConcreteApplicaiton : public application {

....

 

 

의 구조일 경우 어플리케이션에서는 생성되는 (포인터)오브젝트들에 대해 리스트, 벡터등의 자료 구조로 가지고 있지만

 

생성은 application 에서 하지 않고 ConcreteAppliation 에게 생성 부분을 위윔하게 하고

 

ConcreteApplication 클래스에서 생성된 클래스의 함수들이 Application 의 virtual 함수르 통해 실행 시키는 구조

 

 

- 구조정리 : Application 클래스를 경유하도록 하지만 관리를 위해 Application 클래스 내에 오브젝트들의 포인터를 가지고 있는 구조

 

=> 각 A, B, C 라는 독립적으로 돌아가는 프로세스가 있을때 각각에 대한 것들을 각각 관리하길 원할때

 

A 에 관련된 모든 사항을   A 에서 통제하길 원하고

 

B 에 관련된 모든 사항을 B 에서 통제하길 원할때

 

 

---->Create명령  >  ---------------  > A.Create()   ---> A 가 사용하게 될 관련 클래스 생성

---->Copy 명령  >  ---------------  >  | A 에 게 Copy 명령이 전달   |---------------  > A 에 의한 A 관련 Copy 명령 처리

 

---->Create명령  >  ---------------  > B.Create()    ---> B 가 사용하게 될 관련 클래스 생성

---->Copy 명령  >  ---------------  >   | B 에 게 Copy 명령이 전달   |---------------  > A 에 의한 A 관련 Copy 명령 처리

 

 

 

본래의  Create 명렬을 A 가 받아 A 가 돌리게 될 관련 클래스들을 A가 대신 생성하는(위임받아 생성) 형식

A 에 의해 생성된것들은 A가 관리한다

B 에 의해 생성된것들은 B가 관리한다

 

 

 

 

 

 


 

 

 

 

 

장점 : 수정이 유연하다, 변경이 필요할 경우 기존에 생성된 것들에 대한 클래스 정의 없이 필요한 부분만 작성하면 되기때문

          다른 패턴에 비해 덜 복잡하다

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Singleton 패턴 , n 개 까지만 생성하고 싶다  (0) 2012.11.02
Prototype 패턴  (0) 2012.11.02
Proxy 패턴  (0) 2012.11.02
flyweight 패턴  (0) 2012.11.02
Facade 패턴  (0) 2012.11.02

 

 

 

 

 

 

이미지들이 있을때

 

이것을 실제 로딩할때만 로딩하고 그렇지 않은 상황 예를 들어 스크롤을 빨리 내려 이미지를 로딩할 필요는 없고

 

이미지에 대한 크기 같은 정보만으로 충분할때나 어떤 접근 제어적인 측면에서

 

Proxy 패턴이 쓰일 수 있다

 

 

인터넷을 한다고 할때

 

 

house  ------------- korean    -------------------- american

 

 

에서 korean 이 proxy 에 해당 한다

 

house 에서 인터넷을 할 경우 미국에 있던 정보를 korean 이 가지고 있다가(일부분 대략적인 정보) 완전한 데이터를 원하는 것이 아니라면

 

일부분의 정보를 코리아에서 하우스로 보내주는 것이 효과적일 것이다

 

이런 객체에 대한 접근을 제어하는 패턴을 Proxy 패턴이라고 한다

 

 

 


 

 

 

굉장히 많은 사용자들이 만화를 보려고 만화 서버에 달려 들었다 하지만

 

매번 필요한 만화를 서버의 하드에서 읽어오면 서버는 빨리 고장 날것이다

 

그렇다면 how?

 

 

중간에 중계기를 하나 두고 중계기는 엄청난 메모리를 가지고 있다라고 하자

 

이때 이 메모리에는 최근 만화에 대한 정보를 위주로 담고 있다고 한다면

 

대부분 사용자들의 최근 만화만 보기에 이 메모리에서 검색해서 중계기가 보내줄 가능성이 높아진다

 

 

만약 구만화를 보는 사용자가 있을 경우엔 메모리에 없을 가능성이 높음으로

 

서버 하드에 가서 읽어서 중계기가 보내줘야 한다

 

 

 

이때이 중계기의 개념이 Proxy 이다

 

반응형

 

 

 

 

 

 

객체들의 동일한 부분을 관리

 

조만한 객체마다 동일한 리소스등을 각각 가지고 있을때( 하지만 똑같은 정보 또는 다른 정보도 있다 각 객체마다)

 

동일한 것에 대해 메모리 낭비가 심하다

 

 

이를 해결 하기 위한 패턴

 

 

 

how?

 

객체마다  똑같은건 공유한다,  포인트 같은 걸로

다른것 extrinsic state 공유 불가능 한것

같은것 instrinsic state 공유 가능한 것

 

같은 것을 공유할 수 있도록 한다

 

공유되지 않는 것은 따로 객체로 가져가도록 한다

 

 

그래서 클래스 구조는?

 

 

검색되는 속도는 일반적인 것보다 느릴 수 있다 하지만

메모리는 일반적인 경우보단 절약 될 수 있다

계산에 의해 알오바야 함

 

composite 패턴의 경우 공유될 가능성이 많다

 

 

Flyweight 패턴을 써서 메모리가 줄어들지 않느다면 쓸 필욘 없다

 

 

객체가 공유되어지면 Client 에서는 이 객체들을 생성하면 좋지 않다 왜냐하면 메모리 공유 관리가 어려워지기 때문

 

싱글톤으로 이런 문제를 해결 해 볼 수도 있다

 

 

 

캐릭터의 동일한 bmp 파일을 Flyweight 패턴으로 공유해볼 수 있다

 

반응형

 

 

 

 

 

 엔진을 사용할때 전체 다 알 필요 없이 요약된 필요한 것들을 모아놓은것이 있으면 그것만 알면 대부분

 

돌릴 수 있다 하면 그런 중간자가 있다면 편할 것이다 그 중간자를 가르켜 Facade 패턴이라 하며

 

만약 엔진이 플렛폼 별로 다르고 다르다, 환경별로 facade 도 달라져야 한다면

 

Facade를 Abstract base class 로 만들어 상속 구조로 제공 할 수도 있다

 


 

 

서브시스템내의 객체를 변경 시키는 방법은 ?

 

Facade 생성시 Facade 에 서브시스템내의 객체를 연결 시켜준다

 

 

 


 

 

 

그런데 Abstract Factory 패턴으로 Facade 패턴을 대신해서 플렛폼 별 엔진 클래스들을 생성할 수 있지만

 

Abstract Factory 패턴을 쓰지 않는 다고 하면

 

Abstract base class 로 이루어진 Facade 패턴의 해당 플렛폼에 엔진 내부의 서브시스템을 복제(prototype) 해서 Facade 에 넘겨 줘서

 

Facade 가 서브 시스템을 사용 하게 할 수도 있다

 

(엔진의 내부 서브 시스템 이므로 클라이언트 단에서 엔진 내부를 변경 하는 것을 일반적으로 허용 하지 않으므로 복제해서 넘긴다)

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Proxy 패턴  (0) 2012.11.02
flyweight 패턴  (0) 2012.11.02
Decorator(=wrapper) 패턴  (0) 2012.11.02
composite 패턴  (0) 2012.11.02
Bridge 패턴  (0) 2012.11.02

 

 

 

 

 

 

 동적으로 객체를 추가 할때

 

윈도우창에서 창이 길어지면 없던 스크롤바가 생기듯이

 

 

어떤 오브젝트에 그다음 어떤 오브젝트를 추가 연결 하고 싶은 경우 데코레이터 패턴을 쓸 수 있다

 

 

그다음 오브젝트를 추가 하기 위해 기존에 로딩해 놓고 있는 것이 아닌

 

그 즉시 생성해서 연결해 주는 것이다

 

( 미리 로딩해놓고 연결해 놓고 스위치로 온오프 하는 것이 아님 )

 

 

이때 데코레이터의 역할음 기존에 존재 하던 것에 추가로 포장할것에 대한 포인터를 가지고 있다는 것이다

 

[추가된 것C] --->[추가된 것B] --->[추가된 것A] ---> 베이직객체

 

과 같은 것

 

데코레이터를 통해서 다음 추가된 오브젝트로 이동한다

 

전체 다 실행 시키려면  reculsive 하게 실행 할 수 있다

 

 

 


 

 

주의 : 데코레이터 패턴 자체가 베이직 객체와 추가되는것들과의 구분을 하지 않고 동일하게 사용(동일한 구조 or 함수) 하고 싶은 것인데

 

혀재 노드가 베이직 객체 이냐  또는 B 객체이냐를 따져서 사용하게 하면 데코레이터 패턴에 위배 된다,

 

이런 상황이라면 뭔가 다른 패턴을 찾아 봐야 할지도 모른다)

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

flyweight 패턴  (0) 2012.11.02
Facade 패턴  (0) 2012.11.02
composite 패턴  (0) 2012.11.02
Bridge 패턴  (0) 2012.11.02
adapter 패턴  (0) 2012.11.02

 

 

 

 

 

 이미 존재하는 오브젝트들과 이들을 그룹으로 묶고 있는 Comosite 객체가 있을때

 

가각의 오브젝트나 그룹을 묶고 있는 Comoposite 객체를 동일한 방법으로 사용하려고 하는 패턴

 

 

 

그룹별로 묶여 있는 상황에서 A,B,C 를 묶고 있는 어떤 composite 가 A 를 제외한 B,C 만 묶고자 한다

 

how?

 

 상위 composite 에서 둘로 나누어 다시 composite 을 한다 A 를 갖는 composite 을 사이에 하나 두고

 

B,C 는 기존 A,B,C 를 갖고 있던 composite 가 원래 상위 composite 와 자신과의 중간단계의 composite 를 두어서

 

중간단계가 자신의 composite 에 포함되게 한다

 

 

 

각 노드의 부모로 가려면?

 

방법 :. 각 노드의 부모 객체를 찾게 하기 위해서 부모의 포인터를 갖게 한다

 

이러면 루트노드에서 본다면 루트노드의 자식이 많아질 수록 루트의 포인터 개수 또많 비례하게 생성되야 한다

 

 

----------------------------------------------------------

부모 노드를 가질때는

 

같은 레벨의 오브젝트는 자신을 포함하는 composite 의 포인터를 갖지고 있고

 

같은 레벨의 composite 가 부모 포인터를 가지고 있는 형식

 

----------------------------------------------------------

 

 

 

 

 

그렇다면 이럴 해결하기 위한 방법은?

 

방법 : 루트에 대한  첫번째 차일드는 최소 1개다

          루트는 첫번째 차일드만 가지고 있고 첫번째 차일드는 자신의 자식과 동등한 레벨의 포인터를 가지고 있는 형태로 구성

 

이렇게 하면 첫번째를 통해서 같은 레벨의 끝까지 가려면 곧바로 내려 가는 것보다는 시간이 좀 걸릴 수 있다 (상황에 따라)

 

부모의 자식은 오브젝틀르 묶고 있는 composite 를 자식으로 설정한다

 

 

이 방법은 차일드 간의 순서가 있는 경우 이러한 연결이 유리하다

( B-Tree, B+ tree 등 소팅 되어야 할경우 )

 

 

 

composite 패턴이 기본적으로 트리 형태가 된다

 

Tip : 환원적인 데이터구조 형태는 잘 쓰지 않는다 , 별로 않좋다 , 환원적이지 않으면서 복합적인 형태를 다를 수 있는 것이 트리 형태

( 사이클이 없는 솔루션이 좋다 )

 

 

각 노드를 하나씩 지나가는 것은 iterator 패턴을 활용

 

 

 

leaf node(마지막 노드) 의 경우 공유할 가능성이 높다

 

flyweight 패턴으로 이를 해결 한다

 

 

 

 

 

그룹을 갖고 있는 composite 객체는 다른 오브젝트를 추가 하거나 제거 할 수 있지만

 

 

composite 에 추가 되어 있는 leafnode 는 추가하거나 삭제하는 일이 벌어진다면

 

에러가 난다 ( 만약 leafnode가  add ,remove 함수를 composite 처럼 갖고 있지 않다면 )

 

 

how?

 

모든 노드가 add, remove 함수를 갖게 하고 compoiste 일때만 add, remove 를 정의 해주고

 

일반 노드인 경우(클래스) 아무일도 않하면 된다

 

 

 

 

 

그래서 상위 클래스의 public 은 하위 노드에서 모두 갖게 해야 한다( 그것이 올바른 디자인 )

 

왜냐하면 composite 객체를 변경해야 하는 상황이 올 경우 동일한 인터페이스가 아니면

 

부모를 자식 composite 객체로 변경 시켜야 하는 상황이 종종 있는데 이때 위험을 앉고 다운 케스팅 해야 하는 상황이 올 수 있기 때문

 

 

 

 

공유되는 것의 소멸은 레퍼런스 카운트로 해결

 

 

 

 

 

DOM 구조

 

<table>

  <th>   

        <td>  </td>

  </th>

 </table>

 

 

이런 구조가 결국 트리인데 이것을 DOM 구조라 한다

 

동일한 인터페이스와 그룹으로 이루는 것들을 compoiste 패턴으로 다룰 수 있다

 

 

 

트리로 나오는 것은 거의다 composite 패턴으로 다룰 수 있다

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Facade 패턴  (0) 2012.11.02
Decorator(=wrapper) 패턴  (0) 2012.11.02
Bridge 패턴  (0) 2012.11.02
adapter 패턴  (0) 2012.11.02
visitor 패턴  (0) 2012.11.02

 

 

 

 

 

 

어뎁터 패턴은 이미 만들어져 있는 코드를 붙이는 것

 

-브릿지 패턴은 아직 만들기 이전에 내가 구현과 인터페이스를 아에 나누어 버리겠다는 것

-여러플렛폼에서 동일한 부분이 아닌  다른 부분만을 서브클래스로 두어 구현하고자 할때 


 

 

여러가지 환경에서 실행소스를 최대한 건드리지 않고 인터페이스에서 실행 부분을 접근해 사용 하는 방법

 

 

 

 인터페이스 부분 따로 구현 부분 따로 구분해서

 

구현 부분을 인터페이스 쪽에서 알지 못하게 사용, 대략 주어지는 것만 사용

 

이때 연결은 포인터로 해야 하는 데 그 이유는 연결되는 실행 클래스의 하위 클래스 까지 접근하기 위해

 

 

 

 

 

 

 


 

 

구현부분을 종료 시키는 시점은 구현을 참조하는 인터페이스의 레퍼런스 카운트를 둬 카운트가 제로가 될때

 

 

 


 

 

 

구현 부분 클래스를 상속관계로 둬서 자식들의 여러 플렛폼 환경을 각각 정의 하고 있을때 해당 플렛폼에 대한 클래스를 선택하게 하는 방법은

 

 

처음부터 인터페이스에게 어떤 것들이 있다라고 알려주고 그 중에서 인터페이스 쪽에서 클래스를 생성하는 방법

 

 

또는 환경 조사 후 그 값에 의한 어떤 기준에 의해서 선택

 

또는 Abstrac Factory 패턴으로 생성자체를 인터페이스가 아닌 다른곳에 위임시키는 방법

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

어떤 문자열을 어떤 문자엔진들 사이에서 검색 하려고 할때 어떤 기준으로 해당 엔진을 선택할 것인가?

 

등에 사용 될 수 있다

 

 

인터페이스에서 검색할 문자열을 엔진쪽에 넘겨주면 엔진은

 

검색 조건에 맞는 문자엔진을( 문자엔진의 서브 클래스 에게 위임시켜 생성하여 return) 리턴 받아 해당 문자엔진의 검색함수를

 

실행시켜 찾아올 수 있다

 

이때 인터페이스는 어떤 문자 엔진을 사용했는지 알 필요도 알아도 안되는 상황에 쓰이는 이런 중간자 역활을 하는 것을 브릿지 패턴이라 한다

 

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Decorator(=wrapper) 패턴  (0) 2012.11.02
composite 패턴  (0) 2012.11.02
adapter 패턴  (0) 2012.11.02
visitor 패턴  (0) 2012.11.02
Template Method 패턴  (0) 2012.11.02

 

 

 

 

 

두가지 정도로 나뉘는데

 

클래스 어뎁터 패턴과

 

오브젝트 어뎁터 패턴으로 나뉜다

 

 

클래스 어뎁터 패턴은

 

A 클래스에서 잘 정의된 B 클래스의 함수등을 사용 하고 싶을때

 

다음과 같이 상속받아 사용 하는 패턴을 말한다

 

class A  : private B{

 

}

 

private 로 클래스 상속을 받게 되면 접근제어자가 B 에서 private 였던것 외에는 모두 private 되면서 상속 되기 때문에

 

오직 A 클래스 에서는 상속받은 B를 내부에서만 사용 할 수 있게 되는 구조가 된다

 

B 의 private 멤버는 상속(엄밀히 말하자면 접근)이 되지 않는다

(원래 private 로 접근제어된 멤버는 상속이 되지 않는다)

 

 

프로젝트를 동시에 진행하다가 잘 만들어진 클래스의 동일한 기능을 쓸때 이와 같은 패턴을 사용 할 수 있으며

 

 

상속구조가 복잡해지면서 다이아몬드 상속구조 같은 상황이 발생하거나 상속과정이 복잡해져서 클래스 어댑터 패턴을 사용 할 경우

 

여기저기서 중복이 일어나면서 상속을 받게 될 것 같다면

 

오브젝트 어뎁터 패턴을 쓰는 것이 낫다

 

 

오베즉트 어뎁터 패턴은 아래 처럼 사용 된다

 

class A{

 

private :

A* m_adapterA;

public :

A(){

 m_adapterA = new A;

}

  ... 자세한 처리는 생략

}

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

composite 패턴  (0) 2012.11.02
Bridge 패턴  (0) 2012.11.02
visitor 패턴  (0) 2012.11.02
Template Method 패턴  (0) 2012.11.02
strategy 패턴  (0) 2012.11.02

 

 

 

 

 

 

일반적 상속구조의 클래스에서

 

각 노드별 하는 overriding 된 함수들이 각각 의 일로 정의되어서 돌아가고 있는데

 

새로운 함수 mm() 을 이 상속 구조에 추가 하고 싶은 경우 ( 또한 노드는 추가 될 수 있다 )

 

 

이런 경우 굉장히 많으 클래스를 추가 시켜야 한다

 

고정된 부분을 기준으로 클래스를 만들다 보니 이런 결과가 나타난다


 

 

how?

 

동적으로 변동 되는 부분을 분리 시킨다!

 

작업의 대상이 되는 것과 작성이 분리 시킨다

 

 

각각 클래스를 따로 만든다

 

visitor class, Make class( 작업 대상이 되는 클래스)

 

 

작업종류 클래스를 visitor class

 


 

만약 트리구조가 있고 그 트리의 노드에는 각각 어떤 타입이 있는지 모를때 이 타입이 무엇인지 체크 하고 싶을때

 

각 노드를 방문하면서 체크해야 한다, ( 각각을 방문한다고해서 visotor 패턴이라 함, 자세한 내용은 계속.. )

 

 

how 방문?

 

 

========================================================

 

visitor* vpCk = new TypeCheckingVisitor ;

 

ConcreteNode->Accept(vpCk);

 

========================================================

 

 

class Node{

   virtual void Accept( visitor* vp )=0;


};

 

로 정의 해놓고

 

서브 노드(Node class 의 서브클래스 )  에 대한 Aceept 가 불릴때 visitor 의 서브 클래스를 넘겨줘

 

class ConcreteNode : public Node {

    void Accept( visitor* vp ){ 

         vp->whatisType(this);

    }

}

 

이렇게 해당 알고리즘을 돌린다

 

이때 알고리즘을 돌리는 클래스(visitor) 쪽에서는 어떤 알고리즘을 돌리는 지를 알고  node 타입(업케스팅된) 의 this  포인터로

 

node 정보를 받기 때문에 이 정보로 알고리즘을 돌리면 된다

 

========================================================

 

class visitor

{

 virtual int whatisType( Node* np )=0;

};

 

class TypeCheckingVisitor : public visitor

{

  virtual int whatisType( Node* np ){

    //타입 체크 정의 부분

  }

};

 

 

 

 

 


 

 

 

근대 위 구조 또한 동일하게 반복될 수 있다 각 노드마다 동일하게 돌아 가는 것이니

동일한 수행 구조라면

 

visitor* vpCk = new TypeCheckingVisitor ;

ConcreteNode->Accept(vpCk);

 

에서 만약 노드마다 어떤 변수에 값을 더해주는 연산을 해야 한다라고 하면 값을 연산해야 하는 visitor 서브 클래스를 만들고

visitor* vpCk = new AddVisitor;

ConcreteNode->Accept(vpCk);

 

이렇게 동일한 구조로 호출해주면 된다

 


 

즉 만들어진 내부구조를 건드리지 않아도 된다는 것이 visitor 패턴의 장점

 

그래서 작업대상은 별로 변경이 이루어지지 않는다

 

그에반해 하고 싶은 작업은 계속 추가 될 수 있다, (추가도 용이하니...)

 

 

- 또한 작업이 한쪽으로 모아진다

 


 

 

일반적인 부모에 추가 하는 상태는 고정되는 상태일때 유용하다

 

 

 


 

 

단점 : 작업 대상이 변경되면  visitor 쪽에서는 거의다 수정해야 됨 !!!!!

대상의 변경이 많을 경우 일반적인 상속구조로 가야 한다

 


 

 

 

STL 컨테이너와 알고리즘을 돌릴 클래스를 따로 작성해 둘을 조합하면서 컨테이너를 순회 하면

이것 또한 visitor 패턴의 응용이 된다

이때 Shared_ptr 사용이 유용할 수 있다 , slice 방지

 

 


 

-interpreter 패턴( 간략한 문법체크 등등의.. )에도 visitor 패턴을 적용 시킬 수 있다

 

-visitor 클래스에서 불리는 알고리즘을 부모에서 정의 할때 인자로 받는 클래스 노드명을

서브노드명으로 고정시켜 다른 것은 받지 않고 해당 노드에 대해 고정된 알고리즘만 돌아가도록

할 수도 있다

 


 

composite 패턴과도 쓰일 수 있다

 

 

class CompositeNode : public Node{

  public :

  virtual void Accept(Visitor& );

  private :

   List<Node*> m_chidren;

}

void CompositeNode::Accept(Visitor& v){

   list<Node*> node(m_children);

 

  for( node.begin(),node.isDone();node.Next(){

    node.CurrentItem()->Accept(v);

  }

  v.VisiteCompositeNode(this);

 

}

 

이때 composite 패턴이 트리와 같은 구조를 갖을 수 있는데 이때 이 노드들을 순회 하는 방법은

 

1. 객체 구조에서 세부 객체들을 찾아 다니면서 Accept() 를 부르는 방법

 

2. visitor 클래스의 연산(함수에) 순회 알고리즘을 적용 할 수 있다

    이때는 순회 하기 위한 알고리즘 중복이 있을 수 있는데, 피할 수 있으면 좋지만 대채로

    노드를 순회방식을 변경할 경우이런 방식을 적용한다

 

3. 별도의 iterator 객체를 생성하여 순회 하도록 하는 경우

    internal iterator  or external iterator 중 효율이 좋은 것으로 돌린다

 

 


 

 

 double dispatch : A 에서 B 를 불렀는데 B 에서 A 를 다시 불러 실행 하는 것

 

Point : 작업종류가 늘어나도 상관없게 하고 싶다

노드는 고정

 

IRC (internet Relay chatting ):

   게임을 하려면 동일한 서버에 붙어야 게임을 할 수 있다

   그런데 이것을 하나의 서버에 있는 것처럼 게임을 할 수 있게 해주는 것

   서버구조 간에 스페닝 트리 구조를 만들고 각 서버 간의 정보를

   서버에서 서버로 옮겨줘 해당 유저 정보를 복사 받는다(실제로는 엄청 느림)

 

  이때 visitor 를 따로 정의 하면

 

RoomListVisitor, ParthnerListVisitor

 

등을 만들어 해당 서버(노드) 에 관한 정보를 받아 리턴해주는 구조를 만들 수 있다

결과는 각 서버(노드)에서 Accept() 에서 수행된 결과를 Accept 안에서  return 해주는 형태로 줄 수도 있다

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

Bridge 패턴  (0) 2012.11.02
adapter 패턴  (0) 2012.11.02
Template Method 패턴  (0) 2012.11.02
strategy 패턴  (0) 2012.11.02
state 패턴  (0) 2012.11.02

 

 

 

 

 

 

Template method 패턴 - 공통된 알고리즘 부분이 고정적일때 사용

 

기본 알고리즘 골격을 상위 클래스에서 갖도록 한다


 

 한글 ,오피스 ,엑셀등의 문서를 여는 함수의 모듈 Open() 은 비슷하다 거의 똑같다고 볼 수도 있는데

 

이런 공통과정을 클래스에게 맏기는 패턴

 

ABC 를 상위클래스 하나의 정의에 의하여 공유하고 , 각

하위 응용 프로그램마다(서브클래스마다) 파일을 여는 과정을 새로 정의 하는 것

 

전체적인 알고리즘은 공유하고 각 세부사항 구현은 따로 구현 하는 방식

 

전반적인 알고리즘 구조를 공유하는 모든 클래스의 부모 클래스에 정의 해놓고

 

이 부모 클래스에 동일한 알고리즘 을 둔다 그다음 서브 클래스의 세부 알고리즘은 Overriding 되어 구현 한다

 

 

 

부모 A 클래스에서 virtual void OpenDocument(); 함수가 정의 하여 모든 응용프로그램에 대한 문서를 여는 공통 알고리즘으로

 

부모 에 정의 해 놓고

 

만약 A 클래스내의 OpenDocument 안에서 이파일은 열 수 있는 문서라는 것이 확인 되어 진다면 해당 응용프로그램에 대해서

문서를 열도록 한다

 

처음 생성 클래스를 서브 클래스로 만들어 부모 A 클래스로 업캐스팅 한다음 실행을 OpenDocument 를 불러주면

부모에만 이 함수가 있음으로 부모의 OpenDocument 를 불렀다가 나머지 실제 여는 부분은

Overriding 되어 있음으로 서브 클래스의 함수로 호출 되는 구조가 된다

 

이때 OpenDocument 멤버 함수를 Template Method 라 한다

 

 


 

 

Template method 패턴은 - 공통된 부분이 고정적일때 사용

 

-알고리즘 중 변하지 않는 부분을 한번만 구현해 둔다음 변할 수 있는 부분을 하위 클래스에 의해 구현 되도록 할때 사용

 

-하위 클래스 확장을 제어하고 싶을때 Template Method 패턴을 사용 할 수 있다

 

 

 


 

 

대부분 자식이 불린후 자식에서 부모를 부르는 연산 순서를 취하는데

 

자식에서 부모함수명에 대한 부모클래스명:: 을 빼 먹는 경우 함수명이 수행함수명과 동일한 경우 엉뚱한 결과를 낳을 수 있는데

 

이런 위험을 없애려면 Template method 패턴을 경우 먼저 함수를 부를때 부모를 먼저 부른 후

 

부모에서 수행해야할 연산을 먼저 수행한 후 자식에 대한 HookOperation() => (이 함수는 자식에 overriding 된 함수) 함수를 수행하면

 

자식의  HookOperation() 이 불리는 순서가 역순인

 

Hock 연산을 사용 할 수 있다

 

 

 

 


 

 

Strategy 패턴은 객채를 변경 시켜가며 변경되는 패턴이고

 

Template Method 는 상속 관계를 이용해서 알고리즘의 일부분을 변경 시켜주는 것

 

그리고 Template Method 패턴은 Factory Method 패턴을 사용 할대가 종종 있다(자주)

 

이때 이 함수들은 굳이 public 으로 둘 필요는 없다( 클라이언트로부터 제어 하기 위한 구조)

 

Template method 에 의해 불려지는 함수(연산) 들은 protected 로 제어될 수 있지만

 

Template method 는 virtual 로 선언되어져서는 안된다!!

 


 

 

Template method 안에서 불리는 기본연산(함수) 를 최대한 적게 정의 해야 되는데

 

기본 연산이 많아 질수록 overriding 해야 할 함수들이 많아짐으로 구현이 지저분해진다

 

 


 

 

 

네트웍에서

 

Mke request

send request

new response

Process message

 

등이 있을때

 

HTTP, FTP, SMTP 등등 에서

 

등에서 위의 대략 4가지는 공통적이다

 

즉 구체적인 구현은 달리질 수 있지만 전체적인 과정은 동일 할 경우

 

 

상위  ClientComunication 클래스를 두고

 

하위 HTTP ,FPT ,SMT 클래스를 두어

 

세부적으로 위 4개의 함수를 Overriding 하여 세부적으로 구현

 

 

또한

send request

new response

 

에 대한 ABC 구조를 따로 만들어 추가 할 수도 있다

 

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

adapter 패턴  (0) 2012.11.02
visitor 패턴  (0) 2012.11.02
strategy 패턴  (0) 2012.11.02
state 패턴  (0) 2012.11.02
Observer 패턴  (0) 2012.11.02

 

 

 

 

 

 

같은 목적을 수행하는 알고리즘을 동적으로 바꾸어 가며 사용하는 패턴

 


 

 

문자를 잘라주는 알고리즘이 있는데 문자를 한번에 한문자씩, 한라인씩 한 문단씩 또는 동일 단어씩으로

 

잘라주는 비슷한 알고리즘이 알고리즘있고 이를 가변적인 상황에서 동적으로 어떻게 설계할 것인가

 

how?

 

1. 각 case 별로 해당 알고리즘을 불러준다 => 후에 추가하면 나중에 case 문을 건드려야 하고, 하드코딩 되어 있으니 무뭔가

    각 케이스안에서 전 후에 작업을 추가 할 경우도 건드려야 한다 => 좋지 않음

 

2. 처음 클라이언트가 어떤 알고리즘을 설정할때는 case 문이 들어갈 순 있지만

    실행시에는 단한줄로 case 없이 실행 시키려면

    부모 Abstract Base class 를 두고 알고리즘별 서브 클래스를 둔다

    부모 ABC 에는 virtual void execute()=0; 정도로 공통 인터페이스를 두면 된다

 

A::Action( Strategy_AbstractAction* p ){

  p->Execute();

}

 


 

원래 본체 실행 구문에서 때어놓아 따로 구현 하는 것이 좋다

=> Strategy* 를 어느 클래스나  실행 함수내의 등에서 포함관계 시켜 놓는 것이 좋다

context 클래스내에 같이 구현해 버리면 동적으로 교체를 못한다, 또한 구현이 복잡해 진다

 

Strategy 패턴은 변하는 것과 변하지 않는 것을 구분하고 변하는 것들에 대해서 위처럼 정의 한다

 

캐릭터의 동작을 동적으로 바꾸려면 클라이언트에서 class 만  부모 포인터로 할당해주면 된다

 

 


 

C 에서도 어떤 정렬 알고리즘을 하나로 묶기 위해 함수포인터를 사용 할 수 있다

하지만 다른 사람이 볼때 처음에 분석시 애매할 수 있다

 

하지만 클래스의 경우 클래스 명이 있기에 구분이 명확하다

 


 

어떤 알고리즘을 클라이언트가 알 필요가 없을때

case 문으로 작성할 경우 인자들을 써야 하는 경우가 생기기 때문에 Strategy 패턴으로 사용 시 유용

실행구문을 간단하게 처리 할 수 있다

 

공통된 알고리즘으로 추출해서 ABC 에 놓을 수 있다

알고리즘도 보호할 수 있는데 실행 되는 것은 ABC 구조내에서 사용 함으로 클라이언트를 알 필요가 없다

그럼으로 함수를 protected:  로 선언 할 수 있다

 

 

 

 


 

근대 공통된 인터페이스 이므로 공통 수행 함수에 아규먼트가 틀리면, 이에 대한 오버헤드가 있을 수 있다

 


 

context 클래스와 Strategy 는 서로 데이터를 쉽고 효율적으로 전달 할 수 있어야 한다

 

멤버함수로 필요한 데이터를 덭져 주는 것은 그나마 결합도를 떨어뜨릴 수 있는데

 

context class 의 데이터가 필요해서 context* 를 알고리즘인 Strategy 에 포함 시켜 버리고

해당 알고리즘 자식 클래스가 이 포인터를 사용하게 되는 경우

자식클래스가 context 를 다 알아버리는 꼴이 됨으로 결합도가 올라감으로

왼만하면 멤버함수로 값을 던져 주는게 좋다

 

또는 ->Execute() 를 수행하면서 이 함수에 인자를 넘겨도 되긴하지만 상속받는 전체 함수에 동일한 인자를 써야 한다는 것과

나중에 수정이 생기면 이부분의 상속 함수 인자부분을 전체 건드려야 하는 상황이 발생 함으로 판단하에 사용할 것

 


 

template 으로 실행할 알고리즘의 클래스를 작성할 수 있다

그런데 이경우 먼저 하위 클래스를 고정해 버림으로 동적으로 알고리즘을 바꾸기는 힘들다

static(고정) 하게 되어버려 template 을 써도 괜찮은지 한번 생각해 봐야 한다

 

 

 

 


 

 

 

 

 

알고리즘의 경우 요소 개수에 따라 빠른 알고리즘이 다를때 Strategy 패턴을 사용 하면 효과적이다

 

 

 

반응형

'디자인패턴과방법론 > 디자인패턴' 카테고리의 다른 글

visitor 패턴  (0) 2012.11.02
Template Method 패턴  (0) 2012.11.02
state 패턴  (0) 2012.11.02
Observer 패턴  (0) 2012.11.02
memento 패턴  (0) 2012.11.02

+ Recent posts