Na tentativa de entender completamente como resolver os vários problemas de herança do Java, tenho uma pergunta clássica que preciso ser esclarecida.
Vamos dizer que eu tenho classe Animal
isto tem subclasses Bird
e Horse
e eu preciso fazer uma classe Pegasus
que se estende a partir Bird
e Horse
uma vez Pegasus
é ao mesmo tempo um pássaro e um cavalo.
Eu acho que esse é o problema clássico dos diamantes. Pelo que entendi, a maneira clássica de resolver isso é criar as interfaces Animal
, Bird
e Horse
classes e implementar a Pegasus
partir delas.
Fiquei me perguntando se havia outra maneira de resolver o problema em que ainda posso criar objetos para pássaros e cavalos. Se houvesse uma maneira de criar animais, isso seria ótimo, mas não necessário.
public class Pegasus extends Horse implements Flying
.Respostas:
Você pode criar interfaces para classes de animais (classe no significado biológico), como
public interface Equidae
para cavalos epublic interface Avialae
pássaros (não sou biólogo, portanto os termos podem estar errados).Então você ainda pode criar um
e
e também
Adicionando a partir dos comentários:
Para reduzir o código duplicado, você pode criar uma classe abstrata que contenha a maior parte do código comum dos animais que você deseja implementar.
Atualizar
Eu gostaria de adicionar mais um detalhe. Como Brian observa , isso é algo que o OP já sabia.
No entanto, quero enfatizar que sugiro ignorar o problema da "herança múltipla" com interfaces e que não recomendo o uso de interfaces que já representem um tipo concreto (como Bird), mas mais um comportamento (outras se referem a digitação de pato, o que também é bom, mas quero dizer apenas: a classe biológica dos pássaros, Avialae). Também não recomendo o uso de nomes de interface que começam com um 'I' maiúsculo, como, por exemplo
IBird
, que não diz nada sobre o motivo de você precisar de uma interface. Essa é a diferença para a pergunta: construa a hierarquia de herança usando interfaces, use classes abstratas quando útil, implemente classes concretas quando necessário e use delegação, se apropriado.fonte
AbstractHorse
, que também pode ser usado para construir zebras ou outros animais semelhantes a cavalos.AbstractEquidae
seria mais adequado do queAbstractHorse
. Seria estranho ter uma zebra estendendo um cavalo abstrato. Bela resposta, a propósito.Duck
implementação de classeAvialae
?Existem duas abordagens fundamentais para combinar objetos:
A maneira como isso funciona é que você tem um objeto Animal. Nesse objeto, você adiciona outros objetos que fornecem as propriedades e comportamentos necessários.
Por exemplo:
Agora,
IFlier
fica assim:Então
Bird
é assim:Agora você tem todas as vantagens da herança. Você pode reutilizar o código. Você pode ter uma coleção de IFliers e pode usar todas as outras vantagens do polimorfismo, etc.
No entanto, você também tem toda a flexibilidade do Composition. Você pode aplicar quantas interfaces diferentes e classe de apoio composto desejar para cada tipo de
Animal
- com o controle necessário sobre a configuração de cada bit.Padrão estratégico Estratégia de abordagem alternativa à composição
Uma abordagem alternativa, dependendo do que e como você está fazendo, é fazer com que a
Animal
classe base contenha uma coleção interna para manter a lista de comportamentos diferentes. Nesse caso, você acaba usando algo mais próximo do padrão de estratégia. Isso oferece vantagens em termos de simplificação do código (por exemplo,Horse
não é necessário saber nada sobreQuadruped
ouHerbivore
), mas se você também não faz a abordagem da interface, perde muitas das vantagens do polimorfismo, etc.fonte
getFlier()
deve ser reimplementado para todo e qualquer tipo de pássaro.MathsTeacher
eEnglishTeacher
ambos herdarTeacher
,ChemicalEngineer
,MaterialsEngineer
etc herdarEngineer
.Teacher
eEngineer
ambos implementamComponent
. OPerson
então só tem uma lista deComponent
s, e você pode dar a eles osComponent
s certos para issoPerson
. ou sejaperson.getComponent(Teacher.class)
,person.getComponent(MathsTeacher.class)
etc.Eu tenho uma ideia estúpida:
fonte
Posso sugerir o conceito de digitação de pato ?
Muito provavelmente você tenderia a fazer o Pegasus estender uma interface Bird e Horse, mas a digitação de pato na verdade sugere que você deve herdar um comportamento . Como já foi dito nos comentários, um pégaso não é um pássaro, mas pode voar. Portanto, seu Pegasus deve herdar uma
Flyable
interface e digamos umGallopable
interface.Esse tipo de conceito é utilizado no Padrão de Estratégia . O exemplo dado realmente mostra como um pato herda o
FlyBehaviour
eQuackBehaviour
e ainda pode haver patos, por exemplo, oRubberDuck
que não pode voar. Eles também poderiam terDuck
estendido aBird
classe -classe, mas teriam desistido de alguma flexibilidade, porque todosDuck
seriam capazes de voar, até os pobresRubberDuck
.fonte
Tecnicamente falando, você pode estender apenas uma classe de cada vez e implementar várias interfaces, mas ao colocar a mão na engenharia de software, prefiro sugerir uma solução específica para um problema que geralmente não é responsável. A propósito, é uma boa prática OO, não estender classes concretas / apenas estender classes abstratas para evitar comportamentos indesejados de herança - não existe um "animal" e nenhum uso de um objeto animal, mas apenas animais concretos.
fonte
No Java 8, que ainda está na fase de desenvolvimento em fevereiro de 2014, você pode usar métodos padrão para obter uma espécie de herança múltipla semelhante a C ++. Você também pode dar uma olhada neste tutorial, que mostra alguns exemplos que devem ser mais fáceis de começar a trabalhar do que a documentação oficial.
fonte
É seguro manter um cavalo em um estábulo com meia porta, pois um cavalo não pode passar de meia porta. Por isso, montei um serviço de alojamento para cavalos que aceita qualquer item do tipo cavalo e o coloco em um estábulo com meia porta.
Então, um cavalo é como um animal que pode voar até um cavalo?
Eu costumava pensar muito em herança múltipla, mas agora que programa há mais de 15 anos, não me importo mais em implementar herança múltipla.
Freqüentemente, quando tentei lidar com um design que apontava para várias heranças, mais tarde cheguei à conclusão de que não entendi o domínio do problema.
OU
fonte
Java não tem um problema de herança múltipla, pois não possui herança múltipla. Isso ocorre por design, a fim de resolver o problema real de herança múltipla (O problema do diamante).
Existem diferentes estratégias para mitigar o problema. O mais imediatamente possível é o objeto Composite que Pavel sugere (essencialmente como o C ++ lida com ele). Não sei se a herança múltipla via linearização C3 (ou similar) está nos cartões para o futuro do Java, mas duvido.
Se sua pergunta é acadêmica, a solução correta é que Pássaro e Cavalo são mais concretos e é falso supor que um Pegasus é simplesmente um Pássaro e um Cavalo combinados. Seria mais correto dizer que um Pegasus tem certas propriedades intrínsecas em comum com Pássaros e Cavalos (ou seja, eles talvez tenham ancestrais comuns). Isso pode ser suficientemente modelado como aponta a resposta de Moritz.
fonte
Eu acho que depende muito das suas necessidades e de como suas classes de animais devem ser usadas no seu código.
Se você quiser usar métodos e recursos de suas implementações de cavalos e aves em sua classe Pegasus, poderá implementar o Pegasus como uma composição de um pássaro e um cavalo:
Outra possibilidade é usar um sistema de componentes de entidade abordagem de vez de herança para definir seus animais. Obviamente, isso significa que você não terá classes Java individuais dos animais, mas elas serão definidas apenas por seus componentes.
Algum pseudocódigo para uma abordagem de sistema de componentes de entidade pode ser assim:
fonte
você pode ter uma hierarquia de interface e depois estender suas classes a partir de interfaces selecionadas:
e defina suas classes conforme necessário, estendendo uma interface específica:
fonte
IBird
eIHorse
deve implementar emIAnimal
vez deAnimal
Ehm, sua classe pode ser a subclasse de apenas 1 outra, mas ainda assim, você pode ter quantas interfaces implementadas, conforme desejar.
Um Pegasus é de fato um cavalo (é um caso especial de cavalo), capaz de voar (que é a "habilidade" deste cavalo especial). Por outro lado, você pode dizer que o Pegasus é um pássaro, que pode andar e tem 4 patas - tudo depende de como é mais fácil escrever o código.
Como no seu caso, você pode dizer:
fonte
As interfaces não simulam herança múltipla. Os criadores de Java consideraram a herança múltipla incorreta, portanto não existe tal coisa em Java.
Se você deseja combinar a funcionalidade de duas classes em uma composição de objeto de uso único. Ou seja,
E se você deseja expor certos métodos, defina-os e deixe-os delegar a chamada ao controlador correspondente.
Aqui, as interfaces podem ser úteis - se
Component1
implementa interfaceInterface1
eComponent2
implementaInterface2
, você pode definirPara que você possa usar objetos de forma intercambiável onde o contexto permitir.
Então, no meu ponto de vista, você não pode entrar no problema dos diamantes.
fonte
Como você já deve saber, a herança múltipla de classes em Java não é possível, mas é possível com interfaces. Você também pode considerar o uso do padrão de design da composição.
Eu escrevi um artigo muito abrangente sobre composição há alguns anos ...
/codereview/14542/multiple-inheritance-and-composition-with-java-and-c-updated
fonte
Dê uma olhada no exemplo abaixo para entender melhor
Quando usar o padrão do decorador?
fonte
Para reduzir a complexidade e simplificar o idioma, a herança múltipla não é suportada em java.
Considere um cenário em que A, B e C são três classes. A classe C herda as classes A e B. Se as classes A e B tiverem o mesmo método e você o chamar de objeto de classe filho, haverá ambiguidade para chamar o método da classe A ou B.
Como os erros em tempo de compilação são melhores que os erros em tempo de execução, o java renderiza o erro em tempo de compilação se você herdar 2 classes. Então, se você tem o mesmo método ou diferente, haverá um erro de tempo de compilação agora.
fonte
Para resolver o problema da herança múltipla em Java → a interface é usada
Notas do J2EE (JAVA principal) do Sr. KVR Página 51
fonte