Problema del diamante
En los lenguajes de programación orientada a objetos, el problema del diamante es una ambigüedad que surge cuando dos clases B y C heredan de A, y la clase D hereda de B y C. Si un método en D llama a un método definido en A, ¿por qué clase lo hereda, B o C?
Por ejemplo, en el contexto del desarrollo de software GUI, una clase Botón
puede heredar de las clases Rectángulo
(para la apariencia) y Ratón
(para los eventos de ratón), y las clases Rectángulo
y Ratón
heredan a su vez de la clase Objeto
. Si ahora el método EsIgualA
es llamado para un objeto Botón
y ese método no está definido en la clase Botón
pero sí para Rectángulo
y también para Ratón
, ¿cuál de los dos métodos debería ser finalmente llamado?
Se llama el problema del 'diamante' por la forma del diagrama de herencia de clase en esta situación. La clase A está arriba, B y C están separadas debajo de ella, y D se une a las dos en la parte inferior consiguiendo la forma de un diamante.
Propuestas
[editar]Diferentes lenguajes de programación tratan este problema de maneras diferentes:
- C++ por defecto sigue cada ruta de herencia por separado, por lo que un objeto D realmente contendría dos objetos A separados, y el uso de los miembros de A debe ser adecuadamente definido. Si la herencia de A a B y la herencia de A a C están marcadas como "virtuales" ("class B : virtual A"), C++ se preocupa por crear sólo un objeto A, y el uso de los miembros de A funciona correctamente. Si herencia virtual y herencia no virtual son mezcladas, hay un solo A virtual y un solo A no virtual para cada ruta de herencia no virtual a A.
- Common Lisp intenta ofrecer tanto el comportamiento por defecto como la capacidad de redefinirlo. Por defecto, se escoge el método con las clases argumento más específicas ; es decir, en el orden en que las clases padre son nombradas en la definición de la subclase. Sin embargo, el programador puede redefinir esto, dando un orden de resolución de método específico o estableciendo una regla para la combinación de métodos.
- Eiffel maneja esta situación seleccionando y renombrando directivas, donde los métodos de los ascendientes a usar en un descendiente son explícitamente especificados. Esto permite que los métodos de la clase base sean compartidos entre sus descendientes o incluso dar a cada uno de ellos una copia separada de la clase base.
- Perl y Io manejan esto mediante la especificación de las clases de herencia en una lista ordenada. En la ambigüedad mostrada arriba, la clase B y sus ascendientes serían comprobados antes de la clase C y sus ascendientes, por lo que el método A sería heredado a través de B.
- Python tiene que tratar con esto desde la introducción de clases de nuevo estilo, de las que todas tienen un ascendiente común,
object
. Python crea una lista de clases que se buscan de izquierda a derecha y de abajo a arriba (D, B, A, C, A) y luego elimina todas las apariciones de una clase repetida menos la última. Por lo que, el orden de resolución del método es: D, B, C, A.
Otros ejemplos
[editar]Lenguajes que sólo permiten herencia simple (como Objective-C, PHP, C#, y Java) permiten la herencia múltiple de interfaces (llamadas protocolos en Objective-C). Las interfaces son esencialmente clases base abstractas con todos los métodos abstractos y sin miembros de datos. El problema es por tanto evitado ya que siempre hay sólo una implementación para un método o propiedad específico y no surge ninguna ambigüedad.
El problema del diamante no está limitado a herencia. También surge cuando archivos de cabecera A, B, C y D incluyen ("#include") los unos a los otros en un diamante como arriba y cabeceras precompiladas separadas son creadas desde B y C. Si estas dos cabeceras precompiladas son combinadas, las declaraciones en A son duplicadas y la convención "#ifndef" no es efectiva. También se encuentra al componer pilas de middleware; por ejemplo, si A es una base de datos y B y C son cachés, D puede pedir tanto a B como a C la confirmación de una transacción, dando lugar a llamadas de confirmación a A duplicadas.