Domanda

Credo di aver chiesto alcune domande simili prima, ma mi è stato tergiversare. Credo che questo sia il vero problema che posso non del tutto laico di riposare.

ho a che fare con un terzi biblioteca , e c'è un oggetto che non può creare per sé , b2Body. Il b2World deve un'istanza . Io personalmente non piace questo modello di progettazione molto; Credo che il b2Body dovrebbe essere in grado di esistere indipendentemente dal mondo, e poi essere aggiunto al mondo quando necessario. Comunque, ho avvolto b2Body con la mia classe di classe, Body, perché ho bisogno di aggiungere alcune cose in più per lo stesso. Allo stesso modo, ho un involucro World. Ora immagino che ho 3 opzioni:

  1. costruttore Sono di Body richiede un puntatore ad World in modo che possa essere completamente istanziati (chiamate b2World::CreateBody da qualche parte dentro) - vale a dire avere un costruttore come Body *b = new Body(world_ptr)
  2. Passo Body a qualche metodo World::CreateBody come come la biblioteca fa già è - vale a dire Body *b = world.CreateBody(params);
  3. duplicare tutti i dati in b2Body in modo che si può utilizzare come vuoi, e poi, dopo aver aggiunto al mondo sarà 'passare' per utilizzare i dati b2Body -. Cioè Body b e world.addBody(b) tardi

(1) e (2) significa che non si può avere un Body senza World, che probabilmente non hanno bisogno, ma potrebbe essere bello avere questa opzione [in modo che posso usare come modello per altri scopi e tale]. Non so cosa gli altri pro e contro ci sono. (3) sembra più bello, ma è molto più lavoro da implementare, e questo significa che devo per duplicare la maggior parte dei dati che è già contenuto nel b2Body.

Quali sono i tuoi pensieri? Io CW questo solo in modo che nessuno si agita.


Non riesco ancora a porre questo a riposare. Questo è ciò che ciascuna delle opzioni sarà simile:

Opzione 1: (quello che preferisco)

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

Opzione 2: (da qualche parte nel mezzo)

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

Opzione 3: (come Box2D fa)

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

Opzioni 2 e 3 non sono così diversi, ma cambia chi ha il controllo sta creando gli oggetti.

Come potrei effettivamente implementare l'opzione 3 però? World::CreateBody() deve chiamare b2World::CreateBody(args) che chiama b2Body::b2Body() e restituisce b2Body ma non chiama mai Body::Body(args) che è un problema. Il b2Body otterrebbe inizializzato completamente, ma il mio involucro non ha posto per farlo è cosa ... Più in particolare, come avrei scritto World::CreateBody(const BodyDef &bd)? Supponendo BodyDef ereditato da b2BodyDef, Corpo da b2Body, Mondo da b2World, ecc.

È stato utile?

Soluzione

Credo che, se avete intenzione di utilizzare una libreria di terze parti, si deve combattere solo il suo disegno se avete un motivo molto meglio di oh, non mi piace quel modello di progettazione molto . La vostra biblioteca ha un modo di fare le cose - a quanto pare, utilizzando un oggetto in fabbrica -. E la lotta che aumenterà la vostra complessità del codice, possibilmente sostanzialmente

Altri suggerimenti

Suona come l'oggetto b2World è una fabbrica per b2Body, così l'autore ha deciso che un b2Body non ha alcun significato senza il suo mondo.

La mia prima reazione sarebbe che questo è l'interfaccia, in modo da vivere con essa. Avere il vostro oggetto del mondo sia una fabbrica per il vostro corpo. Ecco, questo è vicino ad avvicinarsi (1), tranne che non si dispone di un costruttore pubblico, l'oggetto del mondo ha un metodo makeBody ().

Si pensa che Corpi senza Mondiale hanno senso? Se è così, forse quello che si trova è che alcuni sottoinsieme di metodi di corpo potrebbe essere utile senza un mondo, io non sono chiare come li si implementano - che chiaramente non possono essere attuate da b2Body, perché non può esistere senza un b2World . Quindi, una possibilità è che si dispone di una serie di informazioni di configurazione

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

Ora, questi (o ad est le bgetters) chiaramente potrebbe avere senso, con o senza mondo.

Con questo in mente, credo che si potrebbe scoprire che avete actualy due "stati" di corpo, uno quando non è associato con World, uno quando si tratta. E le capacità reali sono diverso . Quindi in realtà hanno due interfacce.

Quindi, avere una classe IndependentBody e una classe corpo. Il metodo di fabbrica del mondo potrebbe avere una firma

World {

    Body makeBody(IndependentBody);

}

In seguito il tuo link, vedo che createBody non restituisce un b2Body, ma un puntatore a uno:

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

Questo è dovuto probabilmente al fatto b2World

  1. gestisce il ciclo di vita b2Body ( cioè. , lo cancella e la memoria usa quando il B2World va fuori del campo di applicazione / si sé cancellata), o

  2. Perché il B2Wsorld ha bisogno di mantenere puntatori a b2Bodies, per es. per scorrere su di loro per eseguire alcune funzionalità B2World.

Noto inoltre il tutto quello che serve (diverso da un b2World) per creare un b2Body è un puntatore a un b2BodyDef.

Quindi, se volete un b2Body che non è collegato a una b2World, ma può ad un certo seguito essere collegato a uno, perché non passare intorno b2BodyDefs, o puntatori a loro?

.

I potrebbe di creare un involucro leggero per un b2BodyDef, es ,

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

Si noti che ho potuto collegare questo b2BodyDefWrapper a più mondi, o allo stesso mondo più di una volta.

Ora può essere che si possono fare le cose ad un b2Body che non si può fare a un b2BodyDef, e in modo che passando intorno (forse avvolto) b2BodyDefs non soddisfare i vostri scopi. In questo caso, potrei utilizzare il modello di comando a "collegare" un elenco di funzioni al b2BodyDefWrapper, che sarebbe "riprodotto" su ogni b2Body reificato:

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

Dove Command è una classe di base astratta per Funtori, in questo modo:

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

con sottoclassi concrete:

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

Poi ho potuto usare il mio involucro in questo modo:

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

Si noti che ho lasciato fuori alcuni dettagli, principalmente sulla proprietà oggetto e la gestione della memoria, e che chiama delete su CommandStacks; Anche io non ho seguito la regola-of-three nei miei schizzi delle classi. È possibile compilare questi come ti piace.

Inoltre ho lasciato fuori qualsiasi disposizione per chiamare, da un comando, le funzioni che restituiscono b2Body diverso da vuoto e la restituzione di quei valori; probabilmente si può coprire questo (nel caso fosse necessario) avendo ApplyTo restituire un'unione di qualche tipo.

Più fondamentalmente, non ho coperto come un comando concreto in grado di fornire il suo valore di ritorno (se presente) per un altro comando di cemento. Una soluzione completa sarebbe quella di avere non un vettore di comandi, ma un n albero ario di loro, in cui i comandi del bambino sono applicate prima, ed i loro valori di ritorno (se presenti) sono in dotazione per il loro comando genitore . Sia che necessità tale complessità è una domanda che, ovviamente, non posso rispondere. (E ho già dato una risposta piuttosto dettagliata, considerato sto né essere pagati per questo, né mi appare punto di reputazione, dal momento che si Community Wiki'd questa domanda.)

Sono d'accordo che non si dovrebbe essere combattendo la progettazione di una libreria di terze parti che si sta utilizzando. Scendendo un tale percorso può causare un sacco di problemi in futuro.

Per guardare "sotto le coperte" e la creazione di involucri, si può essere bloccando il comportamento della biblioteca 3rd party per il modo in cui l'implementazione corrente comporta.

Che cosa accadrà se una futura versione delle API rimane lo stesso, ma la semantica sottostanti cambiare?

Improvvisamente, tutto è rotto dal punto di vista del vostro involucro.

Solo il mio 0,02.

Uno dei motivi che Box2D utilizza gli oggetti bodyDef per costruire oggetti b2Body è così che si può riutilizzare il DEF per creare più corpi. Codice come:

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

è un modo molto efficiente e compatto di creare molti oggetti con le stesse caratteristiche. Non deve essere nello stesso circuito sia, è possibile mantenere gli oggetti Def intorno come metadati, e di creare organi da loro, se necessario.

Non combattere perché è lì per un motivo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top