Frage

Ich glaube, ich habe vor ein paar ähnlichen Fragen gestellt, aber ich war Umschweife. Ich denke, das ist das eigentliche Problem ist, dass ich nicht ganz Laien zur Ruhe.

Ich bin den Umgang mit einem Dritten Bibliothek , und es gibt ein Objekt, das sich nicht erstellen , b2Body. Die b2World muss instanziiert . Ich persönlich mag es nicht, diesen Entwurf Muster sehr viel; Ich denke, die b2Body sollte unabhängig von der Welt existieren kann, und dann in der Welt hinzugefügt werden, wenn nötig. Wie dem auch sei, ich habe eingewickelt b2Body mit meiner eigenen Klasse Klasse, Body, weil ich sowieso es einige zusätzliche Sachen hinzufügen müssen. In ähnlicher Weise habe ich einen World-Wrapper. Nun Ich denke, ich habe 3 Optionen:

  1. Konstruktor Body der nimmt einen Zeiger so World, dass es vollständig instanziiert werden kann (Anrufe irgendwo in b2World::CreateBody) - das heißt haben einen Konstruktor wie Body *b = new Body(world_ptr)
  2. Pass Body zu einem gewissen World::CreateBody Methode, wie, wie die Bibliothek bereits tut es - das heißt Body *b = world.CreateBody(params);
  3. Duplizieren alle Daten in b2Body, so dass Sie es jedoch wünschen Sie verwenden können, und dann, nachdem Sie es auf der Welt hinzufügen wird es ‚umschalten‘ die b2Body Daten zu verwenden, -. Dh Body b und später world.addBody(b)

(1) und (2) bedeutet, dass Sie keine Body ohne World haben kann, die ich wahrscheinlich nicht brauchen, aber es wäre schön, diese Option zu haben, [so, dass ich es als Vorlage verwenden können für andere Objekte und solche]. Nicht sicher, was andere Vor-und Nachteile gibt. (3) scheint schöner, aber es ist viel mehr Arbeit zu implementieren, und es bedeutet, dass ich die meisten Daten duplizieren, die bereits in b2Body enthalten sind.

Was sind Ihre Gedanken? Ich werde dies CW nur so niemand Bünde.


Ich kann immer noch nicht legen diese zur Ruhe. Dies ist, was die einzelnen Optionen aussehen würde:

Option 1: (was ich bevorzuge)

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

Option 2: (irgendwo in der Mitte)

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

Option 3: (wie Box2D tut es)

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

Optionen 2 und 3 sind nicht so verschieden, aber es ändert sich die Kontrolle hat über die Objekte erstellt.

Wie würde ich implementieren tatsächlich Option 3 obwohl? World::CreateBody() hat b2World::CreateBody(args) zu nennen, die b2Body::b2Body() ruft und gibt b2Body aber nie ruft Body::Body(args) was ein Problem ist. Die b2Body würde vollständig initialisiert, aber mein Wrapper hat keinen Platz, es ist, etwas zu tun ... Genauer gesagt, wie würde ich World::CreateBody(const BodyDef &bd) schreiben? Unter der Annahme, BodyDef geerbt von b2BodyDef, Körper aus b2Body, Welt von b2World, etc.

War es hilfreich?

Lösung

Ich denke, wenn Sie eine Drittanbieter-Bibliothek verwenden werden, können Sie nur sein Design kämpfen sollen, wenn Sie einen viel besseren Grund als oh, ich weiß nicht, wie die Design-Muster viel . Ihre Bibliothek hat eine Art und Weise, Dinge zu tun - offenbar durch ein Factory-Objekt mit -. Und Kämpfen, die Ihre Komplexität des Codes zu erhöhen, möglicherweise im wesentlichen

Andere Tipps

Klingt wie das b2World Objekt eine Fabrik für b2Body ist, so der Autor hat entschieden, dass eine b2Body keinen Sinn, ohne seine Welt hat.

Meine erste Reaktion wäre, dass dies die Schnittstelle ist, so mit ihr leben. Haben Sie Ihre Welt Objekt eine Fabrik für den Körper sein. So nah, das ist (1) zu nähern, außer dass Sie nicht über einen öffentlichen Konstruktor verfügen, das Welt-Objekt hat eine makeBody () -Methode.

Sie denken, dass Bodies ohne Welt Sinn machen? Wenn ja, vielleicht, was Sie finden ist, dass einige Teilmengen von Körpern Methoden ohne Welt nützlich sein könnten, ich bin nicht klar, wie man sie umsetzen - sie eindeutig nicht von b2Body umgesetzt werden können, weil er nicht ohne b2World existieren kann . So eine Möglichkeit ist, dass Sie eine Reihe von Konfigurationsinformationen haben

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

Nun ist dieses (oder im Osten der bgetters) eindeutig Sinn mit oder ohne Welt machen könnte.

Mit dem im Verstand, ich glaube, Sie werden feststellen, dass Sie actualy zwei „Zustände“ haben von Körper, eins, wenn es nicht mit der Welt verknüpft ist, ein, wenn es ist. Und die tatsächlichen Fähigkeiten sind unterschiedliche . Daher eigentlich zwei Schnittstellen haben.

So eine IndependentBody Klasse und eine Körperklasse. Die Welt Factory-Methode könnte eine Signatur haben

World {

    Body makeBody(IndependentBody);

}

Im Anschluss an Ihrem Link, ich sehe, dass createBody kein b2Body zurückkehrt, sondern ein Zeiger zu einem:

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

Dies ist wahrscheinlich, weil b2World

  1. verwaltet die b2Body Lebensdauer ( d. , löscht sie und den Speicher verwendet es, wenn die B2World den Gültigkeitsbereich verlässt / selbst gelöscht) oder

  2. Da die B2Wsorld Zeiger auf b2Bodies beibehalten muss, z. iterieren ihnen einige B2World Funktionalität zu erreichen.

Ich stelle fest, auch das alles, was erforderlich (außer einem b2World) eine b2Body zu schaffen, ist ein Zeiger auf eine b2BodyDef.

Wenn Sie also eine b2Body möchten, die nicht auf eine b2World angebracht ist, später aber an einigen einem befestigt werden kann, warum nicht um b2BodyDefs passieren oder Zeiger auf sie?

.

I könnte erstellen eine dünne Wrapper für eine b2BodyDef, z ,

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

Beachten Sie, dass ich diesen b2BodyDefWrapper auf mehrere Welten anhängen könnte, oder auf der gleichen Welt mehr als einmal.

Nun kann es sein, dass Sie die Dinge zu einem b2Body tun können, die Sie nicht auf eine b2BodyDef tun können, und damit um Passing (möglicherweise umgebrochen) b2BodyDefs nicht Ihre Zwecke anpassen. In diesem Fall könnte ich das Command-Muster verwenden, um „anhängen“, um eine Liste von Funktionen an die b2BodyDefWrapper, das wäre „abgespielt“ an jedem verdinglichten 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 );
   }
 }

Wo Command ist eine abstrakte Basisklasse für Funktoren, wie folgt aus:

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

mit konkreten Unterklassen:

 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 ) ;
   }
 }

Dann könnte ich meine Wrapper wie folgt verwenden:

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

Beachten Sie, dass ich habe einige Details ausgelassen, vor allem über das Objekt Eigentum und Speicherverwaltung und dem auf CommandStacks Anrufe löschen; Ich habe auch die Regel-of-drei in meinen Skizzen der Klassen nicht gefolgt. Sie können in diese ausfüllen, wie Sie möchten.

Ich habe auch eine Bestimmung gelassen für den Aufruf von einem Befehl, b2Body Funktionen, die anderen Rück als ungültig und diesen Wert zurückkehrt; Sie können dies wahrscheinlich decken (sollten Sie müssen) durch eine Vereinigung von einer Art ApplyTo zurückkehren zu müssen.

Grundsätzlicher habe ich nicht abgedeckt, wie ein konkreter Befehl ihren Rückgabewert liefern kann (falls vorhanden) an einem anderen konkreten Befehl. Eine vollständige Lösung wäre nicht ein Foto von Befehlen zu haben, aber eine n ary Baum von ihnen, in denen Kinder Befehle zuerst angewandt werden, und deren Rückgabewerte (falls vorhanden) an ihre Mutter Befehl geliefert . Egal, ob Sie Notwendigkeit diese Komplexität ist eine Frage, die ich kann natürlich nicht beantworten. (Und ich habe schon eine ziemlich ausführliche Antwort gegeben, als ich bin weder dafür bezahlt, noch bekomme ich Ruf Punkt, da Sie Gemeinschaft Wiki'd diese Frage.)

Ich bin damit einverstanden, dass Sie sollten nicht den Aufbau einer 3rd-Party-Bibliothek zu kämpfen, die Sie verwenden. Überschrift nach unten einen solchen Weg kann eine Menge Probleme in der Zukunft führen.

Mit einem Blick „unter der Decke“ und die Schaffung von Verpackungen, Sie auf die Art und Weise das Verhalten der 3rd-Party-Bibliothek Sperren werden, in dem die aktuelle Implementierung verhält.

Was passiert, wenn eine zukünftige Version der API gleich bleibt, aber die zugrunde liegende Semantik ändern?

Auf einmal ist alles aus der Sicht Ihrer Wrapper gebrochen.

Nur meine 0,02.

Ein Grund, dass Box2D bodyDef Objekte verwendet b2Body Objekte zu konstruieren, so dass Sie die def mehrere Körper zu schaffen, wiederverwenden können. Code wie:

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)
   }
}

ist eine sehr effiziente und kompakte Art und Weise viele Objekte mit den gleichen Eigenschaften zu schaffen. Dabei spielt es keine entweder in der gleichen Schleife sein müssen, können Sie die def Objekte um als Metadaten halten können, und schaffen Körper von ihnen je nach Bedarf.

Sie es nicht kämpfen, weil sie es für einen Grund ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top