Polimorfismo
Contanto que você use getType()
ou algo assim, você não está usando polimorfismo.
Entendo que você precisa saber que tipo tem. Mas qualquer trabalho que você queira fazer sabendo que realmente deve ser empurrado para a classe. Então você apenas diz quando fazê-lo.
O código processual obtém informações e toma decisões. O código orientado a objetos informa aos objetos para fazerem as coisas.
- Alec Sharp
Este princípio é chamado de dizer, não pergunte . A seguir, ajuda a não espalhar detalhes como digitar e criar uma lógica que atue sobre eles. Fazer isso transforma uma classe de dentro para fora. É melhor manter esse comportamento dentro da classe para que ele possa mudar quando a classe mudar.
Encapsulamento
Você pode me dizer que nenhuma outra forma será necessária, mas eu não acredito em você e nem deveria.
Um bom efeito de seguir o encapsulamento é que é fácil adicionar novos tipos porque seus detalhes não se espalham no código em que aparecem if
e na switch
lógica. O código para um novo tipo deve estar em um só lugar.
Um sistema de detecção de colisão ignorante
Deixe-me mostrar como eu projetaria um sistema de detecção de colisão com desempenho e funcione com qualquer formato 2D, não se importando com o tipo.
Digamos que você deveria desenhar isso. Parece simples. São todos círculos. É tentador criar uma classe de círculo que entenda colisões. O problema é que isso nos leva a uma linha de pensamento que desmorona quando precisamos de 1000 círculos.
Não deveríamos estar pensando em círculos. Deveríamos estar pensando em pixels.
E se eu disser a você que o mesmo código que você usa para desenhar esses caras é o que você pode usar para detectar quando eles tocam ou mesmo em quais deles o usuário está clicando.
Aqui, desenhei cada círculo com uma cor única (se seus olhos forem bons o suficiente para ver o contorno preto, apenas ignore). Isso significa que todos os pixels nesta imagem oculta são mapeados de volta para o que a atraiu. Um hashmap cuida disso muito bem. Você pode realmente fazer polimorfismo dessa maneira.
Essa imagem você nunca precisa mostrar ao usuário. Você o cria com o mesmo código que desenhou o primeiro. Apenas com cores diferentes.
Quando o usuário clica em um círculo, eu sei exatamente qual círculo, porque apenas um círculo é dessa cor.
Quando desenho um círculo em cima de outro, posso ler rapidamente todos os pixels que estou prestes a substituir, despejando-os em um conjunto. Quando terminei os pontos de ajuste para todos os círculos com os quais colidimos e agora só preciso ligar para cada um uma vez para notificá-lo da colisão.
Um novo tipo: Retângulos
Tudo isso foi feito com círculos, mas eu pergunto: seria diferente com retângulos?
Nenhum conhecimento de círculo vazou no sistema de detecção. Ele não se importa com raio, circunferência ou ponto central. Ele se importa com pixels e cores.
A única parte desse sistema de colisão que precisa ser empurrada para as formas individuais é uma cor única. Fora isso, as formas podem apenas pensar em desenhar suas formas. É no que eles são bons de qualquer maneira.
Agora, quando você escreve a lógica da colisão, não se importa com o subtipo que possui. Você diz para colidir e diz o que encontrou sob a forma que está pretendendo desenhar. Não há necessidade de saber o tipo. E isso significa que você pode adicionar quantos subtipos quiser, sem precisar atualizar o código em outras classes.
Opções de implementação
Realmente, não precisa ser de uma cor única. Pode ser referências a objetos reais e salvar um nível de indireção. Mas aqueles não pareceriam tão agradáveis quando desenhados nesta resposta.
Este é apenas um exemplo de implementação. Certamente existem outros. O que isso foi feito para mostrar é que, quanto mais próximo você deixar esses subtipos de formas ficarem com sua única responsabilidade, melhor o sistema inteiro funcionará. Provavelmente, existem soluções mais rápidas e com menos uso intensivo de memória, mas se elas me forçarem a espalhar o conhecimento dos subtipos, eu seria relutante em usá-las mesmo com os ganhos de desempenho. Eu não os usaria a menos que claramente precisasse deles.
Expedição dupla
Até agora, ignorei completamente o duplo envio . Eu fiz isso porque eu podia. Contanto que a lógica da colisão não se importe com os dois tipos colididos, você não precisa dela. Se você não precisar, não use. Se você acha que pode precisar, deixe de lidar com isso o máximo que puder. Essa atitude é chamada YAGNI .
Se você realmente precisar de diferentes tipos de colisões, pergunte a si mesmo se n subtipos de forma realmente precisam de 2 tipos de colisões. Até agora, trabalhei muito para facilitar a adição de outro subtipo de forma. Não quero estragar tudo com uma implementação de despacho duplo que força os círculos a saber que existem quadrados.
De qualquer forma, quantos tipos de colisões existem? Um pouco de especulação (coisa perigosa) inventa colisões elásticas (saltitantes), inelásticas (pegajosas), enérgicas (explosivas) e destrutivas (perigosas). Pode haver mais, mas se isso for menor que n 2, não vamos exagerar nossas colisões.
Isso significa que, quando meu torpedo atinge algo que aceita danos, não precisa SABER que atingiu uma nave espacial. Só precisa dizer: "Ha ha! Você sofreu 5 pontos de dano".
Coisas que causam dano enviam mensagens de dano para coisas que aceitam mensagens de dano. Feito dessa maneira, você pode adicionar novas formas sem informar as outras formas sobre a nova forma. Você acaba se espalhando por novos tipos de colisões.
A nave espacial pode enviar de volta para a rampa "Ha ha! Você sofreu 100 pontos de dano". bem como "Agora você está preso ao meu casco". E a torp pode enviar de volta "Bem, eu terminei, então esqueça de mim".
Em nenhum momento os dois sabem exatamente o que são. Eles apenas sabem como se comunicar através de uma interface de colisão.
Agora, com certeza, o envio duplo permite controlar as coisas mais intimamente do que isso, mas você realmente quer isso ?
Se você, pelo menos, pense em fazer o envio duplo através de abstrações de quais tipos de colisões uma forma aceita e não na implementação real da forma. Além disso, o comportamento de colisão é algo que você pode injetar como dependência e delegar a essa dependência.
atuação
O desempenho é sempre crítico. Mas isso não significa que é sempre um problema. Teste de desempenho. Não basta especular. Sacrificar tudo o mais em nome do desempenho geralmente não leva ao código de execução de qualquer maneira.