Entendendo o Dispatch Múltiplo

5

Eu tenho lido por aí tentando entender vários envios e por que é tão especial.

Na Wikipédia, encontrei este exemplo simples:

 (defmethod collide-with ((x asteroid) (y asteroid))
   ;; deal with asteroid hitting asteroid
   )
 (defmethod collide-with ((x asteroid) (y spaceship))
   ;; deal with asteroid hitting spaceship
   )
 (defmethod collide-with ((x spaceship) (y asteroid))
   ;; deal with spaceship hitting asteroid
   )
 (defmethod collide-with ((x spaceship) (y spaceship))
   ;; deal with spaceship hitting spaceship
   )

O que eu não entendo, no entanto, é como é diferente de fazer algo assim:

class Asteroid {
  def collide(y: Asteroid)  = // deal with asteroid hitting asteroid
  def collide(y: SpaceShip) = // deal with asteroid hitting spaceship
}

class SpaceShip {
  def collide(y: Asteroid)  = // deal with spaceship hitting asteroid
  def collide(y: SpaceShip) = // deal with spaceship hitting spaceship
}

No nível técnico, qual é a diferença? Eu sei que um usa vários despacho, e o outro usa o despacho único com sobrecarga de método.

Pelo que entendi, o padrão de visitante é uma solução para não ter vários despachos em linguagens OOP de distribuição única, o que me confunde mais, já que a solução de sobrecarga parece estar funcionando bem sem empregar esse padrão.

Então alguém pode explicar as diferenças técnicas entre fazer um contra o outro?

    
por Electric Coffee 14.04.2016 / 16:14
fonte

3 respostas

2

Para entender a diferença, há mais uma parte do quebra-cabeça que você precisa entender: envio dinâmico .

Vamos modificar um pouco seu exemplo, tendo ambos Asteroid e SpaceShip herdados de Sprite (como você faria se fosse um jogo), que tem os mesmos dois métodos, mas é abstrato. Agora imagine algum código como:

Sprite ship = new SpaceShip();

Aqui podemos dizer que o tipo estático de ship é Sprite . O compilador não permite passar para um método que requer um parâmetro do tipo SpaceShip() e não permitiria que você chamasse nenhum método que estivesse em SpaceShip , mas não em Sprite .

No entanto, o tipo dinâmico é SpaceShip . O que isso realmente significa? Isso significa que, se você chamar ship.collide(anotherShip) , ele saberá usar SpaceShip.collide em vez de Asteroid.collide ou qualquer outra implementação. Usar o tipo dinâmico para escolher qual método chamar é chamado de "despacho dinâmico".

Então, dado que, a resposta é que o segundo exemplo que você dá não pode lidar com vários despachos baseados nos tipos dinâmicos . Por exemplo,

Sprite ship = new SpaceShip();
Sprite asteroid = new Asteroid();
ship.collide(asteroid);

Isso não será compilado, porque o compilador escolherá apenas um método baseado no tipo dinâmico de ship e o tipo estático de asteroid , e não vai encontrar um.

De acordo com o artigo da Wikipedia, o primeiro exemplo é de uma linguagem que escolhe entre esses quatro métodos com despacho dinâmico. Se o segundo exemplo fosse de alguma linguagem que escolheria entre sobrecargas nessas duas classes com base no tipo dinâmico, então também haveria vários envios dinâmicos, e a diferença que o artigo está tentando ilustrar não existiria. Nesse ponto, a escolha seria sobre decisões de design mais gerais, como as outras respostas descrevem.

    
por 14.04.2016 / 17:14
fonte
3

O problema com a segunda solução é que cada um dos Asteroid e Spaceship precisam de conhecimento um do outro agora, criando uma strong dependência. Se classes adicionais forem misturadas, cada classe pertencente ao grupo precisa ser tocada.

Ter uma classe multiprocessamento central significa que apenas dois arquivos precisam ser modificados, no máximo, para adicionar uma nova classe ao grupo.

Além disso, o multiprocessamento permite refatorar a classe de distribuição para uma generalização genérica mais fraca, mais tarde, sem alterar a API.

    
por 14.04.2016 / 16:50
fonte
0

Não parece diferente quando você o escreve como no seu exemplo simplificado, mas em um aplicativo real, Asteroid e Spaceship estarão em dois arquivos completamente separados, com vários outros métodos não relacionados e dados misturados. O envio múltiplo permite que você agrupe facilmente toda a sua lógica de colisão em um único local, compartilhando funcionalidades comuns relacionadas à colisão, sem a distração de códigos não relacionados a esse propósito.

    
por 14.04.2016 / 16:46
fonte