Pergunta

Eu acho que eu tenho algumas perguntas semelhantes antes, mas eu estava rodeios. Eu acho que este é o verdadeiro problema que eu não consigo colocar para descansar.

Eu estou lidando com um terceiros biblioteca , e há um objeto que não pode criar a si mesmo , b2Body. O b2World tem que instanciá-lo . Eu pessoalmente não gosto deste padrão de design muito; Eu acho que o b2Body deve ser capaz de existir independentemente do mundo, e, em seguida, ser adicionado ao mundo quando necessário. Enfim, eu embrulhado b2Body com minha classe classe própria, Body, porque eu preciso adicionar algumas coisas extra para ele de qualquer maneira. Da mesma forma, eu tenho um invólucro World. Agora eu acho que eu tenho 3 opções:

  1. construtor nos ter Body leva um ponteiro para World para que ele possa ser totalmente instanciado (chamadas b2World::CreateBody em algum lugar dentro) - ou seja, ter um construtor como Body *b = new Body(world_ptr)
  2. Passe Body a algum método World::CreateBody como a forma como a biblioteca já faz isso - ou seja Body *b = world.CreateBody(params);
  3. duplicar todos os dados em b2Body de modo que você pode usá-lo como quiser, e, em seguida, depois que você adicioná-lo ao mundo que vai 'switch over' para utilizar os dados b2Body -. Ie Body b e world.addBody(b) mais tarde

(1) e (2) significa que você não pode ter um Body sem World, que eu provavelmente não vai precisar, mas que poderia ser bom ter essa opção [para que eu possa usá-lo como um modelo para outros objectivos e tais]. Não tenho certeza que outros prós e contras existem. (3) parece mais agradável, mas é muito mais trabalho para implementar, e isso significa que tenho para duplicar a maioria dos dados que já contidos no b2Body.

Quais são seus pensamentos? Vou CW isso apenas para que ninguém trastes.


Eu ainda não pode colocar isso para descansar. Isto é o que cada uma das opções seria parecido com:

Opção 1: (o que eu prefiro)

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

Opção 2: (em algum lugar no meio)

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

Opção 3: (como Box2D faz isso)

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

Opções 2 e 3 não são tão diferentes, mas muda que tem controle sobre é criar os objetos.

Como é que eu realmente implementar a opção 3 embora? World::CreateBody() tem que b2World::CreateBody(args) chamada que chama b2Body::b2Body() e retorna b2Body mas nunca chama Body::Body(args) que é um problema. O b2Body iria ficar totalmente inicializado, mas o meu invólucro não tem lugar para fazê-lo de coisa ... Mais especificamente, como eu ia escrever World::CreateBody(const BodyDef &bd)? Assumindo BodyDef herdada de b2BodyDef, Corpo de b2Body, Mundial de b2World, etc.

Foi útil?

Solução

Eu acho que, se você estiver indo para usar uma biblioteca de terceiros, você só deve lutar seu design se você tiver uma razão muito melhor do que oh, eu não gosto desse padrão de design muito . Sua biblioteca tem uma maneira de fazer as coisas - aparentemente, usando um objeto de fábrica -. E combate que irá aumentar a sua complexidade do código, possivelmente substancialmente

Outras dicas

Parece que o objeto b2World é uma fábrica para b2Body, então o autor decidiu que um b2Body não tem sentido sem o seu mundo.

A minha primeira reação seria a de que esta é a interface, de modo ao vivo com ele. Ter o seu objeto Mundo ser uma fábrica para o seu corpo. Então, isso é perto de abordagem (1), exceto que você não tem um construtor público, o objeto Mundial tem um método makeBody ().

Você acha que os corpos sem mundo faz sentido? Se assim for, talvez o que você encontra é que algum subconjunto de métodos corpo poderia ser útil sem um mundo, eu não sou claro como você implementá-las - eles claramente não pode ser implementado por b2Body, porque ele não pode existir sem uma b2World . Assim, uma possibilidade é que você tem um conjunto de informações de configuração

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

Agora, estes (ou pelo Oriente O bgetters) claramente poderia fazer sentido com ou sem Mundial.

Com isso em mente, eu acho que você pode achar que você actualy têm dois "estados" de corpo, uma quando não está associado com o mundo, uma quando ela é. E as capacidades reais são diferente . Daí você realmente tem duas interfaces.

Então, tem uma classe IndependentBody e uma classe corpo. O método de fábrica Mundial pode ter uma assinatura

World {

    Body makeBody(IndependentBody);

}

A seguir o seu link, vejo que createBody não retorna um b2Body, mas um ponteiro para um:

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

Isto é provável porque b2World

  1. administra a vida b2Body ( i. , exclui-lo ea memória ele usa quando o B2World sai do escopo / é em si excluído), ou

  2. Porque as necessidades B2Wsorld para manter ponteiros para b2Bodies, por exemplo. para iterar sobre eles para realizar algumas funcionalidades B2World.

Eu também notar a tudo que é necessário (que não seja um b2World) para criar um b2Body é um ponteiro para um b2BodyDef.

Então, se você quer um b2Body que não está ligado a uma b2World, mas pode em alguns mais tarde ser anexado a um, por que não passar em torno b2BodyDefs ou ponteiros para eles?

I pode criar um wrapper fino para uma b2BodyDef, por exemplo ,:.

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

Note que eu poderia anexar este b2BodyDefWrapper para vários mundos, ou para o mesmo mundo mais de uma vez.

Agora, pode ser que você pode fazer coisas em um b2Body que você não pode fazer a um b2BodyDef, e de modo que passando em torno de (possivelmente envolto) b2BodyDefs não vai atender às suas finalidades. Neste caso, eu poderia usar o padrão de comando para "anexar" uma lista de funções à b2BodyDefWrapper, que seria "repetido" em cada b2Body reificada:

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

Onde Command é uma classe base abstrata para Functores, como este:

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

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

Então eu poderia usar o meu invólucro assim:

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

Note que eu tenho deixado de fora alguns detalhes, principalmente sobre a posse de objetos e gerenciamento de memória, e que chama de exclusão em CommandStacks; Eu também não seguiu a regra-de-três em meus esboços das classes. Você pode preencher estes como você gosta.

Eu também ter deixado de fora qualquer disposição para chamar, a partir de um comando, as funções b2Body que retornam que não seja nulo e retornar esses valores; provavelmente você pode cobrir este (caso precise) por ter ApplyTo retornar uma união de algum tipo.

Mais fundamentalmente, eu não coberto como um concreto de comando pode fornecer seu valor de retorno (se houver) para outra concreto comando. Uma solução completa seria ter não um vetor de comandos, mas um n árvore -ary deles, onde Comandos criança são aplicadas primeiro, e seus valores de retorno (se houver) são fornecidos ao seu pai Command . Se você necessidade tal complexidade é uma pergunta que eu obviamente não posso responder. (E eu já dei uma resposta muito detalhada, considerada estou nem ser pago por isso, nem estou recebendo ponto de reputação, desde que você Comunidade Wiki'd esta pergunta.)

Eu concordo que você não deveria estar lutando o projeto de uma biblioteca parte 3 que você está usando. Descendo tal caminho pode causar uma série de problemas no futuro.

Ao olhar "sob as cobertas" e criando wrappers, você pode estar bloqueando o comportamento da biblioteca parte 3 para a maneira pela qual se comporta de implementação atual.

O que acontecerá se uma versão futura das estadias API a mesma, mas a mudança semântica subjacente?

De repente, tudo é quebrado a partir do ponto de vista do seu invólucro.

Apenas minha 0,02.

Uma das razões que box2d usa bodyDef objetos para construir b2Body objetos é para que você pode voltar a usar o def para criar múltiplos corpos. 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)
   }
}

É uma maneira muito eficiente e compacto de criar muitos objetos com as mesmas características. Ele não tem que estar no mesmo ciclo, quer, você pode manter o def objetos em torno de como metadados, e criar corpos de-los quando necessário.

Não lute contra isso, porque ele está lá por uma razão.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top