문제

나는 전에 몇 가지 비슷한 질문을했다고 생각하지만, 나는 덤불 주위를 때리고있었습니다. 나는 이것이 내가 휴식을 취할 수없는 진짜 문제라고 생각합니다.

나는 a를 다루고있다 제 3 자 도서관, 그리고 스스로를 만들 수없는 객체가 있습니다. b2Body. 그만큼 b2World 해야합니다 그것을 인스턴스화하십시오. 나는 개인적 으로이 디자인 패턴을별로 좋아하지 않습니다. 제 생각에는 b2Body 세계와 독립적으로 존재할 수 있어야하고 필요할 때 세상에 추가해야합니다. 어쨌든, 나는 포장했다 b2Body 내 수업과 함께 Body, 어쨌든 추가 물건을 추가해야하기 때문에. 마찬가지로, 나는 a World 싸개. 이제 3 가지 옵션이 있다고 생각합니다.

  1. 가지다 Body의 생성자는 포인터를 가져갑니다 World 완전히 인스턴스화 할 수 있도록 (전화 b2World::CreateBody 내부) - 즉, 생성자와 같은 생성자가 있습니다 Body *b = new Body(world_ptr)
  2. 통과하다 Body 일부에게 World::CreateBody 라이브러리가 이미 수행하는 방식과 같은 방법 - 즉 Body *b = world.CreateBody(params);
  3. 모든 데이터를 복제하십시오 b2Body 당신이 원하는대로 사용할 수 있도록, 당신이 세상에 추가 한 후에는 '스위치 오버'를 사용합니다. b2Body 데이터 - 즉 Body b 그리고 나중에 world.addBody(b).

(1) 및 (2) Body a World, 아마 필요하지 않을 수도 있지만, 그 옵션을 갖는 것이 좋을 것입니다. [다른 객체 등의 템플릿으로 사용할 수 있습니다]. 다른 장단점이 무엇인지 잘 모르겠습니다. (3) 더 멋지게 보이지만 구현하는 것이 훨씬 더 많은 작업이므로 이미 포함 된 대부분의 데이터를 복제해야한다는 의미입니다. b2Body.

당신의 생각은 무엇입니까? 아픈 CW 이것은 아무도 걱정하지 않습니다.


나는 아직도 이것을 쉬기 위해 놓을 수 없습니다. 이것이 각 옵션의 모습입니다.

옵션 1: (내가 선호하는 것)

World w;
Body b;
Fixture f;
b.addFixture(f);
w.addBody(b);

Option 2: (중간 어딘가)

World w;
Body b(w);
Fixture f(b);

옵션 3 : : 옵션 3. (Box2D가 어떻게 하는가)

World *w = new World;
Body *b = w->CreateBody(args);
Fixture *f = b->CreateFixture(args);

옵션 2와 3은 그다지 다르지 않지만 제어 기능이있는 사람은 객체를 만드는 것입니다.

그래도 실제로 옵션 3을 구현하려면 어떻게해야합니까? World::CreateBody() 전화해야합니다 b2World::CreateBody(args) 어떤 전화 b2Body::b2Body() 그리고 반환 b2Body 그러나 결코 전화하지 않습니다 Body::Body(args) 문제입니다. 그만큼 b2Body 완전히 초기화 될 것이지만 내 래퍼는 할 곳이 없습니다 ... 더 구체적으로, 어떻게 글을 쓸 것인가? World::CreateBody(const BodyDef &bd)? B2BodyDef, B2Body의 바디, B2world의 세계 등 B2BodyDef에서 BodyDef가 상속되었다고 가정합니다.

도움이 되었습니까?

해결책

타사 도서관을 사용하려는 경우 더 좋은 이유가있는 경우에만 디자인과 싸워야합니다. 오, 나는 그 디자인 패턴을별로 좋아하지 않는다. 도서관에는 공장 대상을 사용하여 일을하는 방법이 있으며, 아마도 코드 복잡성을 크게 증가시킬 수있는 싸움.

다른 팁

B2world 객체와 같은 소리는 B2Body의 공장이므로 저자는 B2Body가 세상이없는 의미가 없다고 결정했습니다.

나의 첫 번째 반응은 이것이 인터페이스라는 것입니다. 당신의 세계 대상을 당신의 몸을위한 공장이되도록하십시오. 따라서 공개 생성자가 없다는 점을 제외하고는 접근 (1)에 가깝습니다. World Object에는 MakeBody () 메소드가 있습니다.

당신은 세상이없는 몸이 의미가 있다고 생각합니까? 그렇다면 아마도 당신이 찾은 것은 신체 방법의 일부 하위 집합이 세상없이 유용 할 수 있다는 것입니다. 나는 당신이 어떻게 구현하는지 명확하지 않습니다. B2world 없이는 존재할 수 없기 때문에 B2Body가 구현할 수 없습니다. . 따라서 한 가지 가능성은 구성 정보 세트가있을 수 있습니다.

 class Body {
        int howBig;
        String name;
        Flavour flavour;
        // and getter/setters
 } 

이제이 (또는 동쪽에서 Bgetters)는 분명히 세상의 유무에 관계없이 이해할 수 있습니다.

이를 염두에두고, 나는 당신이 실제로 당신이 세상과 관련이 없을 때, 하나는 하나의 신체의 두 "상태"를 가지고 있다고 생각합니다. 그리고 실제 기능은 다음과 같습니다 다른. 따라서 실제로 두 개의 인터페이스가 있습니다.

독립 학자 클래스와 바디 클래스가 있습니다. 세계 공장 방법에는 서명이있을 수 있습니다

World {

    Body makeBody(IndependentBody);

}

당신의 링크를 따라, 나는 그것을 본다 createBody B2Body를 반환하지 않지만 a 바늘 하나에 :

 b2Body* b2World::CreateBody  ( const b2BodyDef*  def );     

이것은 B2world 때문일 것입니다

  1. B2Body 수명을 관리합니다 (, B2world가 범위를 벗어나거나 자체적으로 삭제 될 때 사용하는 메모리를 삭제합니다), 또는

  2. B2wsorld는 B2Bodies에 대한 포인터를 유지해야하기 때문에 예를 들어 B2world 기능을 달성하기 위해 반복합니다.

나는 또한 필요한 모든 것을 주목합니다 (다른 것 외에는 b2World)를 만들려면 b2Body A에 대한 포인터입니다 b2BodyDef.

따라서 B2world에 첨부되지 않은 B2Body를 원하지만 나중에는 나중에 하나에 부착 될 수 있습니다. B2BodyDef를 주위로 전달하거나 포인터를 전달하지 않겠습니까?

~할 것 같다 B2BodyDef 용 얇은 래퍼를 만들고 예를 들어,:

 class b2BodyDefWrapper {
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reifyOn( b2World& world) const { 
     return world.CreateBody( b2bodyDef ) ;
   }
 }

이 B2BodyDefWrapper를 여러 세계 또는 같은 세계에 두 번 이상 첨부 할 수 있습니다.

이제 B2BodyDef에게 할 수없는 B2Body에게 일을 할 수 있고, (아마도 포장 된) B2BodyDef가 당신의 목적에 적합하지 않을 수 있습니다. 이 경우 명령 패턴을 사용하여 함수 목록을 b2BodyDefWrapper, 그것은 각각의 refied b2body에서 "재생"될 것입니다.

 class b2BodyDefWrapper {
   private std::vector<Command&> commandStack;
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reify( b2World& world) const { 
     b2body* ret = world.CreateBody( &b2bodyDef ) ;
     for (int i=0; i< commandStack.size(); i++) {
        v[i].applyTo( ret ) ;
     }
     return ret;
   }

   public void addCommand( const Command& command ) {
      commandStack.push_back( command );
   }
 }

어디에 Command 다음과 같이 기능을위한 추상적 인 기본 클래스입니다.

  class Command {
     virtual ~Command() {}
     virtual void applyTo( b2Body* body ) = 0 ;
  }

콘크리트 서브 클래스 :

 class ApplyForce : public Command {
   private const b2Vec2& force;
   private const b2Vec2& point;
   ApplyForce(const b2Vec2& f, const b2Vec2& p) : force(f), point(p) {}
   virtual void applyTo( b2Body* body ) {
      body->ApplyForce( force, point ) ;
   }
 }

그런 다음 다음과 같은 내 래퍼를 사용할 수 있습니다.

extern b2BodyDef& makeb2BodyDef();
b2BodyDefWrapper w( makeb2BodyDef()  ) ; 
ApplyForce a( ..., ... );
w.addCommand( a ) ;
...
b2World myworld;
b2World hisWorld;
w.reifyOn( myWorld ) ;
w.reifyOn( hisWorld) ;

주로 객체 소유권 및 메모리 관리에 관한 몇 가지 세부 사항을 제외하고 CommandStacks에서 Delete에 전화를 걸었습니다. 나는 또한 수업 스케치에서 3 번 규칙을 따르지 않았습니다. 원하는대로 이것을 채울 수 있습니다.

또한 명령에서 B2Body 함수는 무효가 아닌 다른 값을 반환하는 B2Body 기능을 부르기위한 조항을 제외했습니다. 당신은 아마도 어떤 종류의 노조를 반환함으로써 이것 (필요한 경우)을 다룰 수 있습니다 (필요한 경우).

더 근본적으로, 나는 하나의 콘크리트 명령이 다른 콘크리트 명령에 반환 값을 제공 할 수있는 방법을 다루지 않았습니다. 전체 솔루션은 명령의 벡터가 아니라 N-자식 명령이 먼저 적용되는 곳, 반환 값 (있는 경우)이 부모 명령에 제공됩니다. 당신이든 필요 그러한 복잡성은 내가 분명히 대답 할 수없는 질문입니다. (그리고 나는 이미 상세한 답변을 주었고, 나는 이것에 대한 돈을받지 못하고, 당신이 커뮤니티 위키 가이 질문을했기 때문에 나는 명성을 얻지 못하거나 명성을 얻지 못한다고 생각했습니다.)

나는 당신이 사용하고있는 제 3 자 도서관의 디자인과 싸우지 않아야한다는 데 동의합니다. 그러한 길을 따라 가면 미래에 많은 문제가 발생할 수 있습니다.

"커버 아래"를보고 포장지를 만들면 현재 구현이 동작하는 방식으로 제 3 자 라이브러리의 동작을 잠그고있을 수 있습니다.

미래의 API 버전이 동일하게 유지되지만 근본적인 의미론이 변경되면 어떻게됩니까?

갑자기 모든 것이 래퍼의 관점에서 깨졌습니다.

내 0.02 만.

Box2D가 BodyDef 객체를 사용하여 B2Body 객체를 구성하여 DEF를 재사용하여 여러 바디를 생성 할 수 있기 때문입니다. 코드와 같은 코드 :

b2BodyDef myDef;
// fill out def

for (int i=0; i < 100; ++i) {
   for (int j=0; j < 100; ++j) {
      myDef.x = i;
      myDef.y = j
      b2Body* body = world.CreateBody(myDef)
   }
}

동일한 특성을 가진 많은 객체를 만드는 매우 효율적이고 컴팩트 한 방법입니다. 동일한 루프에있을 필요는 없으며 DEF 객체를 메타 데이터로 유지하고 필요에 따라 몸체를 만들 수 있습니다.

이유가 있기 때문에 싸우지 마십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top