En un intento por comprender completamente cómo resolver los problemas de herencia múltiple de Java, tengo una pregunta clásica que necesito aclarar.
Digamos que tengo clase, Animal
esto tiene subclases Bird
y Horse
necesito hacer una clase Pegasus
que se extienda desde Bird
y Horse
ya Pegasus
sea un pájaro y un caballo.
Creo que este es el clásico problema del diamante. Por lo que puedo entender la forma clásica de resolver esto es para que el Animal
, Bird
y Horse
las interfaces de clases e implementar Pegasus
de ellos.
Me preguntaba si había otra forma de resolver el problema en el que todavía puedo crear objetos para pájaros y caballos. Si hubiera una manera de poder crear animales también sería genial, pero no necesario.
public class Pegasus extends Horse implements Flying
.Respuestas:
Podría crear interfaces para clases de animales (clase en el significado biológico), como
public interface Equidae
para caballos ypublic interface Avialae
pájaros (no soy biólogo, por lo que los términos pueden estar equivocados).Entonces aún puedes crear un
y
y también
Agregando de los comentarios:
Para reducir el código duplicado, puede crear una clase abstracta que contenga la mayoría del código común de los animales que desea implementar.
Actualizar
Me gustaría agregar un detalle más. Como comenta Brian , esto es algo que el OP ya sabía.
Sin embargo, quiero enfatizar que sugiero evitar el problema de "herencia múltiple" con las interfaces y que no recomiendo usar interfaces que ya representen un tipo concreto (como Bird), sino más bien un comportamiento (otros se refieren a tipificación de patos, que también es bueno, pero quiero decir simplemente: la clase biológica de las aves, Avialae). Tampoco recomiendo usar nombres de interfaz que comiencen con una 'I' mayúscula, como
IBird
, que simplemente no dice nada sobre por qué necesita una interfaz. Esa es la diferencia con la pregunta: construya la jerarquía de herencia usando interfaces, use clases abstractas cuando sea útil, implemente clases concretas donde sea necesario y use la delegación si corresponde.fuente
AbstractHorse
, que también se puede usar para construir cebras u otros animales similares a caballos.AbstractEquidae
sería más adecuado queAbstractHorse
. Sería extraño que una cebra extendiera un caballo abstracto. Buena respuesta, por cierto.Duck
la implementación de una claseAvialae
?Hay dos enfoques fundamentales para combinar objetos juntos:
La forma en que esto funciona es que tienes un objeto Animal. Dentro de ese objeto, agrega otros objetos que le dan las propiedades y comportamientos que necesita.
Por ejemplo:
Ahora
IFlier
solo se ve así:Así se
Bird
ve así:Ahora tiene todas las ventajas de la herencia. Puedes reutilizar el código. Puede tener una colección de IFliers, y puede usar todas las otras ventajas del polimorfismo, etc.
Sin embargo, también tiene toda la flexibilidad de Composición. Puede aplicar tantas interfaces diferentes y clases de respaldo compuesto como desee a cada tipo de
Animal
, con el control que necesite sobre cómo se configura cada bit.Patrón de estrategia enfoque alternativo a la composición
Un enfoque alternativo que depende de qué y cómo esté haciendo es que la
Animal
clase base contenga una colección interna para mantener la lista de diferentes comportamientos. En ese caso, terminas usando algo más cercano al Patrón de Estrategia. Eso ventajas dan en términos de simplificación del código (por ejemplo,Horse
no necesita saber nada sobreQuadruped
oHerbivore
), pero si no lo hace también el enfoque de interfaz se pierde mucho de las ventajas del polimorfismo, etc.fuente
getFlier()
tiene que ser reimplementado para cada tipo de ave.MathsTeacher
yEnglishTeacher
tanto heredarTeacher
,ChemicalEngineer
,MaterialsEngineer
etc heredaEngineer
.Teacher
yEngineer
ambos implementanComponent
. LaPerson
continuación, sólo tiene una lista deComponent
s, y se les puede dar la derechaComponent
s para quePerson
. es decirperson.getComponent(Teacher.class)
,person.getComponent(MathsTeacher.class)
etc.Tengo una estúpida idea:
fuente
¿Puedo sugerir el concepto de mecanografía de pato ?
Lo más probable es que tengas la tendencia de hacer que el Pegasus extienda una interfaz de Bird y Horse, pero la escritura de pato realmente sugiere que debes heredar el comportamiento . Como ya se dijo en los comentarios, un pegaso no es un pájaro pero puede volar. Entonces su Pegasus debería heredar una
Flyable
interfaz y digamos unaGallopable
interfaz.Este tipo de concepto se utiliza en el Patrón de estrategia . El ejemplo dado realmente muestra cómo un pato hereda el
FlyBehaviour
yQuackBehaviour
aún puede haber patos, por ejemploRubberDuck
, que no pueden volar. También podrían haber hecho que la clase seDuck
extendiera,Bird
pero habrían renunciado a cierta flexibilidad, porque todosDuck
podrían volar, incluso los pobresRubberDuck
.fuente
Técnicamente hablando, solo puede extender una clase a la vez e implementar múltiples interfaces, pero al poner manos a la obra en ingeniería de software, preferiría sugerir una solución específica del problema que generalmente no responde. Por cierto, es una buena práctica de OO, no extender clases concretas / solo extender clases abstractas para evitar comportamientos de herencia no deseados; no existe tal cosa como un "animal" y no se usa un objeto animal sino solo animales concretos.
fuente
En Java 8, que aún se encuentra en la fase de desarrollo a partir de febrero de 2014, podría usar métodos predeterminados para lograr una especie de herencia múltiple de C ++. También puede echar un vistazo a este tutorial que muestra algunos ejemplos con los que debería ser más fácil comenzar a trabajar que la documentación oficial.
fuente
Es seguro mantener un caballo en un establo con media puerta, ya que un caballo no puede pasar más de media puerta. Por lo tanto, configuro un servicio de alojamiento de caballos que acepta cualquier artículo de tipo caballo y lo coloca en un establo con media puerta.
Entonces, ¿es un caballo como un animal que puede volar incluso un caballo?
Solía pensar mucho en la herencia múltiple, sin embargo, ahora que he estado programando durante más de 15 años, ya no me importa implementar la herencia múltiple.
En la mayoría de los casos, cuando he intentado hacer frente a un diseño que apuntaba hacia la herencia múltiple, más tarde he llegado a la conclusión de que no entendí el dominio del problema.
O
fuente
Java no tiene un problema de herencia múltiple, ya que no tiene herencia múltiple. Esto es por diseño, para resolver el verdadero problema de herencia múltiple (El problema del diamante).
Existen diferentes estrategias para mitigar el problema. El más inmediato posible es el objeto Compuesto que Pavel sugiere (esencialmente cómo lo maneja C ++). No sé si la herencia múltiple a través de la linealización C3 (o similar) está en juego para el futuro de Java, pero lo dudo.
Si su pregunta es académica, entonces la solución correcta es que Bird y Horse son más concretos, y es falso suponer que un Pegasus es simplemente un Bird y un Horse combinados. Sería más correcto decir que un Pegaso tiene ciertas propiedades intrínsecas en común con las aves y los caballos (es decir, tienen ancestros comunes). Esto se puede modelar lo suficiente como señala la respuesta de Moritz.
fuente
Creo que depende mucho de sus necesidades y de cómo se usarán sus clases de animales en su código.
Si desea poder utilizar métodos y características de sus implementaciones de Horse and Bird dentro de su clase Pegasus, entonces podría implementar Pegasus como una composición de Bird y Horse:
Otra posibilidad es utilizar un enfoque de Entidad-Componente-Sistema en lugar de herencia para definir sus animales. Por supuesto, esto significa que no tendrá clases Java individuales de los animales, sino que solo se definirán por sus componentes.
Algunos pseudocódigos para un enfoque Entity-Component-System podrían verse así:
fuente
puede tener una jerarquía de interfaz y luego extender sus clases desde las interfaces seleccionadas:
y luego defina sus clases según sea necesario, ampliando una interfaz específica:
fuente
IBird
yIHorse
debería implementar enIAnimal
lugar deAnimal
Ehm, tu clase puede ser la subclase de solo 1, pero aún así, puedes tener tantas interfaces implementadas como desees.
Un Pegaso es, de hecho, un caballo (es un caso especial de un caballo), que puede volar (que es la "habilidad" de este caballo especial). Por otro lado, puede decir que el Pegaso es un pájaro que puede caminar y tiene 4 patas; todo depende de cómo le sea más fácil escribir el código.
Como en tu caso puedes decir:
fuente
Las interfaces no simulan herencia múltiple. Los creadores de Java consideran que la herencia múltiple es incorrecta, por lo que no existe tal cosa en Java.
Si desea combinar la funcionalidad de dos clases en una, use la composición de objetos. Es decir
Y si desea exponer ciertos métodos, defínalos y permita que deleguen la llamada al controlador correspondiente.
Aquí las interfaces pueden ser útiles: si
Component1
implementa la interfazInterface1
y losComponent2
implementosInterface2
, puede definirPara que pueda usar objetos indistintamente donde el contexto lo permita.
Entonces, en mi punto de vista, no puedes meterte en el problema del diamante.
fuente
Como ya sabrá, la herencia múltiple de clases en Java no es posible, pero es posible con interfaces. También puede considerar usar el patrón de diseño de composición.
Escribí un artículo muy completo sobre composición hace unos años ...
/codereview/14542/multiple-inheritance-and-composition-with-java-and-c-updated
fuente
Eche un vistazo al siguiente ejemplo para comprender mejor
¿Cuándo usar el patrón decorador?
fuente
Para reducir la complejidad y simplificar el lenguaje, no se admite la herencia múltiple en Java.
Considere un escenario donde A, B y C son tres clases. La clase C hereda las clases A y B. Si las clases A y B tienen el mismo método y lo llama desde un objeto de clase hijo, habrá ambigüedad para llamar al método de clase A o B.
Dado que los errores de tiempo de compilación son mejores que los errores de tiempo de ejecución, Java genera un error de tiempo de compilación si hereda 2 clases. Entonces, ya sea que tenga el mismo método o diferente, habrá un error de tiempo de compilación ahora.
fuente
Para resolver el problema de la herencia múltiple en Java → se utiliza la interfaz
J2EE (core JAVA) Notas del Sr. KVR Página 51
fuente