Problema del diamante

De Wikipedia, la enciclopedia libre
Saltar a: navegación, búsqueda
Un diagrama de herencia en 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 Botón heredar 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 (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 caches, 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.

Véase también[editar]