La mejor manera de tratar con objeto de que no se puede crear una instancia de sí misma?

StackOverflow https://stackoverflow.com/questions/1482998

  •  18-09-2019
  •  | 
  •  

Pregunta

Creo que le he pedido a algunas preguntas similares antes, pero estaba andarse por las ramas. Creo que este es el verdadero problema que no puedo bastante laico para descansar.

Estoy tratando con un terceros biblioteca , y no hay un objeto que no puede crearse a sí mismo , b2Body. El b2World tiene que instanciarlo . Yo personalmente no me gusta este patrón de diseño mucho; Creo que el b2Body debe ser capaz de existir independientemente del mundo, y luego se añade al mundo cuando sea necesario. De todos modos, me he envuelto b2Body con mi propia clase de clase, Body, porque tengo que añadir algo de material extra de todos modos. Del mismo modo, tengo una envoltura World. Ahora me imagino que tiene 3 opciones:

  1. Han de Body constructor toma un puntero a World para que se pueda crear una instancia totalmente (llamadas b2World::CreateBody en algún lugar dentro) - es decir, tener un constructor como Body *b = new Body(world_ptr)
  2. Pass Body a algún método World::CreateBody como la forma de la biblioteca ya lo hace - es decir, Body *b = world.CreateBody(params);
  3. Duplicar todos los datos en b2Body para que pueda usarlo como quiera, y luego después de agregarlo al mundo que va a 'conmutar' para utilizar los datos b2Body -. Es decir Body b y más tarde world.addBody(b)

(1) y (2) significa que no se puede tener un Body sin World, que probablemente no vuelva a necesitar, pero podría ser bueno tener esa opción [para que pueda utilizarlo como plantilla para otros objetos y tales]. No estoy seguro que otras ventajas y desventajas existen. (3) parece más bonito, pero es mucho más trabajo para poner en práctica, y eso significa que tengo que duplicar la mayoría de los datos que ya está contenida en b2Body.

¿Cuáles son sus pensamientos? Voy a CW esto sólo para que nadie se preocupa.


Todavía no puedo poner esto a descansar. Esto es lo que cada una de las opciones se vería así:

Opción 1: (lo que prefiero)

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

Opción 2: (en algún lugar en el medio)

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

Opción 3: (Box2D cómo lo hace)

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

opciones 2 y 3 no son tan diferentes, pero los cambios que tiene control sobre los objetos está creando.

¿Cómo podría realmente poner en práctica la opción 3, aunque? World::CreateBody() tiene que llamar b2World::CreateBody(args) que exige b2Body::b2Body() y devuelve b2Body pero nunca llama Body::Body(args) que es un problema. El b2Body conseguiría totalmente inicializado, pero mi envoltorio no tiene lugar para hacerlo es que ... Más específicamente, ¿cómo iba a escribir World::CreateBody(const BodyDef &bd)? Suponiendo BodyDef heredado de b2BodyDef, Cuerpo de b2Body, Mundial de b2World, etc.

¿Fue útil?

Solución

Creo que, si se va a utilizar una biblioteca de terceros, sólo se debe luchar su diseño si tiene una razón mucho mejor que oh, no me gusta que el patrón de diseño mucho . Su biblioteca tiene una manera de hacer las cosas - al parecer, mediante el uso de un objeto de fábrica -. Y la lucha que aumentará su complejidad del código, posiblemente sustancialmente

Otros consejos

Sonidos como el objeto b2World es una fábrica de b2Body, por lo que el autor ha decidido que un b2Body no tiene sentido sin su mundo.

Mi primera reacción sería que esta es la interfaz, por lo que vivir con ella. Haga que su objeto de World sea una fábrica para su cuerpo. Por lo que está cerca de acercarse (1), excepto que usted no tiene un constructor público, al objeto de World tiene un método makeBody ().

Se cree que los órganos sin Mundial tienen sentido? Si es así, tal vez lo que se encuentra es que algún subconjunto de métodos cuerpo podría ser útil sin un Mundial, no me queda claro cómo ponerlas en práctica - que claramente no pueden ser implementadas por b2Body, porque no puede existir sin un b2World . Así que una de las posibilidades es que tiene un conjunto de información de configuración

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

Ahora bien, estos (o al este de las bgetters) claramente podría tener sentido con o sin Mundial.

Con esto en mente, creo que es posible que se podía comprender tiene dos "estados" de cuerpo, uno cuando no está asociado con World, uno cuando es. Y las capacidades reales son diferente . De ahí que en realidad tienen dos interfaces.

Así que tener una clase IndependentBody y una clase de cuerpo. El método de fábrica mundo podría tener una firma

World {

    Body makeBody(IndependentBody);

}

Después de su enlace, veo que createBody no devuelve un b2Body, sino un puntero a uno:

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

Esto es probablemente debido a b2World

  1. gestiona el tiempo de vida b2Body ( es decir. , lo borra y la memoria que utiliza cuando el B2World sale del ámbito / es en sí suprimido), o

  2. Debido a que el B2Wsorld necesita para mantener los punteros a b2Bodies, por ejemplo. para iterar sobre ellos para llevar a cabo algunas funciones B2World.

También observo el único que se requiere (que no sea un b2World) para crear un b2Body es un puntero a una b2BodyDef.

Así que si quieres un b2Body que no está adherida a un b2World, pero puede en algún tarde pueden unir a uno, ¿por qué no pasar alrededor b2BodyDefs, o punteros a ellos?

.

podría crear una envoltura delgada para una b2BodyDef, por ejemplo ,

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

Tenga en cuenta que pude conectar esta b2BodyDefWrapper a múltiples mundos, o al mismo mundo más de una vez.

Ahora bien, puede ser que usted puede hacer cosas para un b2Body que no se puede hacer a un b2BodyDef, y por lo que pasa alrededor (posiblemente envuelto) b2BodyDefs no se ajusten a sus propósitos. En este caso, puede ser que utilice el patrón de comandos para "unir" una lista de funciones a la b2BodyDefWrapper, eso sería "repite" en cada b2Body cosificado:

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

Cuando Command es una clase base abstracta para Functors, como esto:

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

a las subclases concretas:

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

Entonces podría usar mi envoltura de la siguiente manera:

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

Tenga en cuenta que he dejado fuera algunos detalles, principalmente sobre la propiedad de objeto y la gestión de memoria, y que llama delete en CommandStacks; Tampoco he seguido la regla de tres niños en mis bocetos de las clases. Puede rellenar estos como te gusta.

También he dejado fuera alguna disposición para llamar, a partir de un comando, funciones que devuelven b2Body que no sea nulo y devolver esos valores; es probable que pueda cubrir este (si necesita) por tener ApplyTo devolver una unión de algún tipo.

Más fundamentalmente, no han explicado cómo un comando concreto puede suministrar su valor de retorno (si los hay) a otro comando concreto. Una solución completa sería que no es un vector de comandos, sino un n árbol ary de ellos, en los que se aplicó por primera vez Comandos niño, y sus valores de retorno (si los hay) se suministran a su mando padres . Tanto si necesidad tal complejidad es una pregunta que, obviamente, no puedo responder. (Y yo ya he dado una respuesta bastante detallada, considerada estoy ni me pagan por esto, ni estoy poniendo punto de reputación, ya que la Comunidad Wiki'd esta pregunta.)

Estoy de acuerdo que no deben luchar el diseño de una biblioteca de 3 ª parte que está utilizando. Bajando por un camino de este tipo puede causar muchos problemas en el futuro.

Al mirar "bajo las sábanas" y la creación de envolturas, que puede ser de bloqueo hacia abajo el comportamiento de la biblioteca tercera parte en la forma en que se comporta la aplicación actual.

¿Qué pasaría si una versión futura de la API sigue siendo el mismo, pero cambia la semántica subyacente?

De repente, todo se rompe desde el punto de vista de su envoltorio.

Sólo mi 0,02.

Una de las razones que Box2D utiliza objetos bodyDef para construir objetos b2Body es para que pueda volver a utilizar la definición para crear múltiples órganos. Código como:

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

Es una forma muy eficiente y compacto de crear muchos objetos con las mismas características. Eso no tiene que estar en el mismo bucle o bien, puede mantener los objetos en torno def como metadatos, y crear cuerpos de ellos, según sea necesario.

No luchar contra él porque está ahí por una razón.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top