inheritance - simple - tipos de herencia multiple en java
¿Cuándo es útil la herencia múltiple? (5)
Ejemplo del mundo real: me he encontrado con una situación en la que la herencia múltiple realmente tenía mucho sentido, es para la implementación en C ++ del Vector Animation Complex .
Es una estructura de datos topológica inmersa en el espacio-tiempo. Conceptualmente, es similar a un gráfico, excepto que en lugar de "nodos + bordes", tiene:
- vértices + aristas + caras en el dominio espacial
- llaves + inbetweens en el dominio temporal
Debido a que la estructura de datos es un producto cruzado entre el espacio y el tiempo, tiene un total de 6 clases:
- KeyVertex
- KeyEdge
- KeyFace
- Entrevertex
- InbetweenEdge
- Entre medio
La forma en que lo implementé es a través de herencia múltiple. Hay una clase base virtual:
class Cell;
Luego, hay tres clases virtuales para implementar el "comportamiento espacial" de estas celdas, y dos clases virtuales para implementar el "comportamiento temporal":
class VertexCell: virtual public Cell;
class EdgeCell: virtual public Cell;
class FaceCell: virtual public Cell;
class KeyCell: virtual public Cell;
class InbetweenCell: virtual public Cell;
Finalmente, las clases no virtuales reales se definen con herencia múltiple:
class KeyVertex: public KeyCell, public VertexCell;
class KeyEdge: public KeyCell, public EdgeCell;
class KeyFace: public KeyCell, public FaceCell;
class InbetweenVertex: public InbetweenCell, public VertexCell;
class InbetweenEdge: public InbetweenCell, public EdgeCell;
class InbetweenFace: public InbetweenCell, public FaceCell;
Además de tener sentido matemática y conceptualmente, tenía dos ventajas prácticas:
Maximizando el código compartido. El intercambio de código también se puede lograr con la composición, pero habría requerido más repetitivo.
Casting a clases de base. A veces, necesito un contenedor grande para administrar todas las celdas, por lo que es genial que todos hereden la
Cell
, ya que puedo tener unvector<Cell*>
y llamar directamente a los métodos virtuales compartidos por todas las celdas (por ejemplo,draw()
). A veces, necesito unvector<VertexCell*>
. A veces, necesito unvector<KeyCell*>
.
De hecho, incluso he escrito contenedores convenientes para poder hacer cosas como:
CellPtrSet cells = getCells();
KeyCellPtrSet keyCells = cells; // Cast and copy those which are actually key cells
En pocas palabras, la herencia múltiple hizo posible tener una API muy agradable y limpia para escribir algoritmos que funcionen con estas celdas. Y refleja muy bien la estructura matemática subyacente.
Esta pregunta ya tiene una respuesta aquí:
- ¿Un uso para herencia múltiple? 12 respuestas
¿Puede proporcionar algunos ejemplos del mundo real cuando el problema se puede abordar más fácilmente utilizando herencia múltiple en lugar de usar la composición u otras alternativas?
¿Cuándo debería uno usar herencia múltiple?
¿Por qué algunos lenguajes admiten herencia múltiple (C ++, Python) y otros no (Java, Ruby)? Quiero decir, en función de qué factores deciden los creadores de lenguajes de programación incluir el soporte para MI o no.
Ejemplos de lenguajes modernos que admiten algún tipo de herencia múltiple son Java y Scala. Java tiene métodos de interfaz predeterminados, ya que Java8 y Scala tienen rasgos que pueden contener métodos y datos.
Ahora, ejemplos de uso:
- Definir métodos útiles que invocan métodos abstractos. Por ejemplo, Collection.removeIf en Java: elimina elementos basados en el predicado proporcionado. Por lo tanto, cada clase que implemente la Colección tendrá el método removeIf.
- Clase dividida con lotes y lotes de métodos públicos en unidades de código más pequeñas. Hay casos en los que la agregación no funciona y realmente necesitamos medio centenar de métodos públicos en una sola clase. La herencia múltiple permite una mejor gestión de dicho código.
- DSLs. Puede agregar DSL a su código heredando de la clase que implementa ese DSL.
- Añade algo de comportamiento a tus objetos. En C ++ puede usar la herencia múltiple para agregar un contador de referencia a cada objeto para la administración de la memoria.
En resumen, la herencia múltiple de código es muy útil. La herencia múltiple de los datos es más cuestionable y debe utilizarse con precaución.
La herencia múltiple se utiliza principalmente para fines de integración, es decir, cuando necesita un objeto que implemente toda la funcionalidad requerida de las clases de las que se deriva. Si esto equivale a una solución "mejor" que a las posibles alternativas, es una cuestión de debate o gusto, por lo que probablemente tenga suerte de que esta pregunta no se haya cerrado debido a que se basa principalmente en la opinión.
Con respecto a los ejemplos,
- este artículo sobre el uso de super () de Python para herencia múltiple muestra un ejemplo de implementación compartida. No puedes tener esto con interfaces o composición,
- un ejemplo similar se explica en la página de Wikipedia sobre herencia múltiple para derivar
Button
fromRectangle
andClickable
(también tiene una larga lista de idiomas que admiten MI), - una respuesta a una pregunta anterior para buenos ejemplos de MI apunta a
Observable
y similares, que al menos Bertrand Meyer afirma que se implementó de forma más limpia que MI (por lo tanto, también es afortunado que esta pregunta no se haya cerrado como un duplicado) .
Desde la experiencia personal, la herencia múltiple puede ser muy útil, pero también puede ser un gran problema fácilmente. La página de Wikipedia analiza el problema de Diamond en cierta medida: se reduce a la pregunta de qué debería suceder si dos o más clases base proporcionan una implementación de algún método. Los idiomas deben definir / implementar una forma de lidiar con esto, normalmente definiendo algún orden de resolución de métodos (por ejemplo, el mro de Python ).
El potencial de conflicto aumenta, por supuesto, con el número de clases base y el número de métodos. Una vez tuve un caso en el que el marco (implementado en Python) que usábamos utilizaba la herencia múltiple de un puñado de clases base para alguna clase de la que habíamos derivado. Entonces podríamos anular felizmente un método heredado sin ser conscientes de ello.
La herencia múltiple, aunque a veces es útil, puede verse como una violación del principio de responsabilidad única : por definición, una clase derivada de múltiples clases base se comportará como cualquiera de las dos clases. Por lo tanto, también es bastante probable que, desde una perspectiva de modelado de datos, también se viole el principio de sustitución de Liskov .
Por lo tanto, creo que los creadores de lenguajes de programación pueden reconocer que la herencia múltiple se considera no exenta de problemas conceptuales y puede requerir un esfuerzo de implementación sustancial al tiempo que proporciona un valor agregado limitado a otras soluciones, pero esa es solo mi opinión personal.
MI o herencia múltiple es un tema de debate muy común, tenga cuidado con lo que pide ... Intentaré darle una breve respuesta, como dijo una vez mi profesor: "La herencia simula la vida real, cuando su objeto en la vida real es hereditario. características también lo hace sus objetos ''. Al final del día, debe pensar en el programador que está utilizando su clase. ¿Necesita saber sus objetos compuestos? o tal vez es mejor que simplemente llame a las funciones de una ''caja cerrada''. el mejor ejemplo es la lavadora, tiene botones y muchas funciones de lavado, pero a usted realmente no le importa lo que está pasando dentro, también lo hace su clase, si lo uso no necesito saber los objetos que lo convierten en el funciones Sería aún mejor si conozco solo la interfaz.
Debe usar la herencia múltiple cuando desee evitar copiar el código y cuando un cambio tendrá que ser arreglado en varios lugares.
Sobre su última pregunta, los idiomas están diseñados de manera diferente debido a muchas razones y será una respuesta larga si comenzamos a compararlos.
Responda primero a la última pregunta: la razón por la que algunos idiomas no admiten la herencia múltiple es porque rara vez es necesario y tiende a hacer que el código sea mucho más complicado en comparación con la composición o la herencia de interfaz múltiple.
Esto se relaciona con sus dos primeras preguntas, ya que es difícil encontrar un ejemplo del mundo real (al menos uno simple) en el que el MI pueda resolver un problema fácilmente. Claramente, los creadores de idiomas donde MI no es compatible piensan que nunca se debe usar. Supongo que la razón por la que algunos idiomas lo admiten es porque los creadores no vieron razón para no hacerlo.
Here hay un enlace a una pregunta frecuente en MSDN que explica por qué C # no admite la herencia múltiple.