Eu sempre gostei da idéia de ter várias heranças suportadas em um idioma. Na maioria das vezes, embora seja intencionalmente perdoado, e a suposta "substituição" são interfaces. As interfaces simplesmente não cobrem a mesma herança múltipla, e essa restrição pode ocasionalmente levar a mais códigos padronizados.
A única razão básica que eu já ouvi sobre isso é o problema do diamante com as classes base. Eu simplesmente não posso aceitar isso. Para mim, isso é muito parecido com "Bem, é possível estragar tudo, então é automaticamente uma má ideia". Você pode estragar tudo em uma linguagem de programação, e eu quero dizer qualquer coisa. Eu simplesmente não posso levar isso a sério, pelo menos não sem uma explicação mais completa.
Apenas estar ciente desse problema é 90% da batalha. Além disso, acho que ouvi algo anos atrás sobre uma solução alternativa de uso geral envolvendo um algoritmo de "envelope" ou algo parecido (isso soa um sino, alguém?).
Em relação ao problema do diamante, o único problema potencialmente genuíno em que consigo pensar é se você está tentando usar uma biblioteca de terceiros e não consegue ver que duas classes aparentemente não relacionadas nessa biblioteca têm uma classe base comum, mas além de documentação, um recurso de linguagem simples pode, digamos, exigir que você declare especificamente sua intenção de criar um diamante antes que ele realmente compile um para você. Com esse recurso, qualquer criação de diamante é intencional, imprudente ou porque alguém desconhece essa armadilha.
Para que tudo isso seja dito ... Existe alguma razão real para a maioria das pessoas odiar herança múltipla, ou é apenas um monte de histeria que causa mais mal do que bem? Existe algo que eu não estou vendo aqui? Obrigado.
Exemplo
O carro estende o WheeledVehicle, o KIASpectra estende o Car and Electronic, o KIASpectra contém o rádio. Por que o KIASpectra não contém eletrônicos?
Porque é um eletrônico. Herança versus composição deve sempre ser um relacionamento é-um vs. um relacionamento tem-um.
Porque é um eletrônico. Existem fios, placas de circuito, interruptores etc. tudo isso para cima e para baixo.
Porque é um eletrônico. Se sua bateria descarregar no inverno, você terá tantos problemas como se todas as suas rodas de repente desaparecessem.
Por que não usar interfaces? Veja o número 3, por exemplo. Não quero escrever isso repetidamente, e realmente não quero criar uma classe auxiliar de proxy bizarra para fazer isso:
private void runOrDont()
{
if (this.battery)
{
if (this.battery.working && this.switchedOn)
{
this.run();
return;
}
}
this.dontRun();
}
(Não estamos discutindo se essa implementação é boa ou ruim.) Você pode imaginar como pode haver várias dessas funções associadas ao Electronic que não estão relacionadas a nada no WheeledVehicle e vice-versa.
Eu não tinha certeza se deveria me ater a esse exemplo ou não, já que há espaço para interpretação lá. Você também pode pensar em termos de Veículo com extensão de avião e FlyingObject e Pássaro com extensão de Animal e FlyingObject, ou em termos de um exemplo muito mais puro.
Traits
- eles agem como interfaces com implementação opcional, mas têm algumas restrições que ajudam a evitar problemas como o problema do diamante.
KiaSpectra
não é um Electronic
; ele tem Electronics, e pode ser um ElectronicCar
(que se estenderia Car
...)