O Java omite a herança múltipla com o objetivo de evitar o objetivo do projeto de manter a linguagem simples .
Gostaria de saber se Java (com seu ecossistema) é realmente "simples". Python não é complexo e tem herança múltipla. Então, sem ser muito subjetivo, minha pergunta é ...
Quais são os padrões de problemas típicos que se beneficiam de um código projetado para fazer uso pesado de várias heranças
multiple-inheritance
codificador de árvore
fonte
fonte
Respostas:
Prós:
Contras:
No C ++, um bom exemplo de herança múltipla usada para compor recursos ortogonais é quando você usa o CRTP para, por exemplo, configurar um sistema de componentes para um jogo.
Comecei a escrever um exemplo, mas acho que vale a pena olhar um exemplo do mundo real. Algum código do Ogre3D usa herança múltipla de uma maneira agradável e muito intuitiva. Por exemplo, a classe Mesh herda de Resources e AnimationContainer. Os recursos expõem a interface comum a todos os recursos e o AnimationContainer expõe a interface específica para manipular um conjunto de animações. Eles não estão relacionados, portanto, é fácil pensar em um Mesh como um recurso que, além disso, pode conter um conjunto de animações. Parece natural, não é?
Você pode ver outros exemplos nesta biblioteca , como a maneira como a alocação de memória é gerenciada de maneira detalhada, tornando as classes herdadas das variantes de uma classe CRTP sobrecarregando new e delete.
Como dito, os principais problemas com herança múltipla surgem da mistura de conceitos relacionados. Isso faz com que a linguagem precise definir implementações complexas (veja como o C ++ permite lidar com o problema do diamante ...) e o usuário não tendo certeza do que está acontecendo nessa implementação. Por exemplo, leia este artigo explicando como ele é implementado em C ++ .
Removê-lo do idioma ajuda a evitar pessoas que não sabem como o idioma é forçado a tornar as coisas ruins. Mas isso força a pensar de uma maneira que, às vezes, não parece natural, mesmo que seja um caso extremo, acontece com mais frequência do que você imagina.
fonte
Existe um conceito chamado mixins que é muito usado em linguagens mais dinâmicas. A herança múltipla é uma maneira pela qual os mixins podem ser suportados por um idioma. Mixins são geralmente usados como uma maneira de uma classe acumular diferentes partes de funcionalidade. Sem herança múltipla, é necessário usar agregação / delegação para obter o comportamento do tipo mixin com uma classe, que é um pouco mais pesada em sintaxe.
fonte
Eu acho que a escolha é baseada principalmente em questões devido ao problema do diamante .
Além disso, muitas vezes é possível contornar o uso de herança múltipla por delegação ou outros meios.
Não tenho certeza do significado da sua última pergunta. Mas se for "em que casos a herança múltipla é útil?", Em todos os casos em que você gostaria de ter um objeto A com funcionalidades dos objetos B e C, basicamente.
fonte
Não vou me aprofundar muito aqui, mas você certamente pode entender a herança múltipla em python através do seguinte link http://docs.python.org/release/1.5.1p1/tut/multiple.html :
...
Este é apenas um pequeno parágrafo, mas grande o suficiente para esclarecer as dúvidas, eu acho.
fonte
Um local em que a herança múltipla seria útil é uma situação em que uma classe implementa várias interfaces, mas você gostaria de ter alguma funcionalidade padrão incorporada a cada interface. Isso é útil se a maioria das classes que implementam alguma interface deseja fazer algo da mesma maneira, mas ocasionalmente você precisa fazer algo diferente. Você pode ter cada classe com a mesma implementação, mas faz mais sentido empurrá-la para um único local.
fonte
Este é apenas um exemplo, mas acho inestimável para melhorar a segurança e atenuar as tentações de aplicar mudanças em cascata em todos os chamadores ou subclasses.
Onde eu achei várias heranças incrivelmente úteis, mesmo para as interfaces mais abstratas e sem estado, é o idioma da interface não virtual (NVI) no C ++.
Eles nem são realmente classes básicas abstratas, mas interfaces que têm apenas um pouco de implementação para impor os aspectos universais de seus contratos, pois não estão realmente estreitando a generalidade do contrato, mas sim aplicando-o melhor. .
Exemplo simples (alguns podem verificar se um identificador de arquivo passado está aberto ou algo parecido):
Nesse caso, talvez
f
seja chamado por mil lugares na base de código, enquantof_impl
é substituído por cem subclasses.Seria difícil fazer esse tipo de verificação de segurança em todos os 1000 locais que ligam
f
ou nos 100 locais que substituemf_impl
.Ao fazer esse ponto de entrada para a funcionalidade não virtual, ele me fornece um lugar central para executar essa verificação. E essa verificação não está reduzindo a abstração nem um pouco, pois está simplesmente afirmando uma pré-condição necessária para chamar essa função. Em certo sentido, é possível reforçar o contrato fornecido pela interface e aliviar o ônus de verificar a
x
entrada para garantir que ela esteja em conformidade com as pré-condições válidas em todos os 100 locais que a substituem.É algo que eu gostaria que toda linguagem tivesse, e também desejasse, mesmo em C ++, que fosse um pouco mais de um conceito nativo (por exemplo: não exigindo que definíssemos uma função separada para substituir).
Isso é extremamente útil se você não fez isso
assert
com antecedência e percebeu que precisava disso mais tarde, quando alguns lugares aleatórios na base de código encontravam valores negativos sendo passados paraf
.fonte
Primeiro: várias cópias da classe base (um problema em C ++) e forte acoplamento entre as classes base e derivadas.
Segundo: herança múltipla de interfaces abstratas
fonte