Casos de uso de herencia múltiple

14

Java omite la herencia múltiple debido a que obvia el objetivo de diseño de mantener el lenguaje simple .

Me pregunto si Java (con su ecosistema) es realmente "simple". Python no es complejo y tiene herencia múltiple. Así que sin ser demasiado subjetivo, mi pregunta es ...

¿Cuáles son los patrones de problemas típicos que se benefician de un código diseñado para hacer un uso intensivo de la herencia múltiple?

    
pregunta treecoder 14.08.2011 - 13:56

7 respuestas

10

Pros:

  1. Algunas veces permite un modelado más obvio de un problema que otras formas de modelarlo.
  2. Si los diferentes grupos tienen un propósito ortogonal, puede permitir algún tipo de composición

Contras:

  1. Si los diferentes padres no tienen un propósito ortogonal, hace que el tipo sea difícil de entender.
  2. No es fácil entender cómo se implementa en un idioma (cualquier idioma).

En C ++, un buen ejemplo de herencia múltiple que se usa para componer funciones ortogonales es cuando se usa CRTP para, por ejemplo, configurar un sistema de componentes para un juego.

Empecé a escribir un ejemplo, pero creo que vale la pena ver un ejemplo del mundo real. Algunos códigos de Ogre3D utilizan herencia múltiple de una manera agradable y muy intuitiva. Por ejemplo, < a href="https://bitbucket.org/sinbad/ogre/src/b91d204db480/OgreMain/include/OgreMesh.h"> Mesh la clase hereda tanto de Resources como de AnimationContainer. Los recursos exponen la interfaz común a todos los recursos y AnimationContainer expone la interfaz específica para manipular un conjunto de animaciones. No están relacionados, por lo que es fácil pensar en una malla como un recurso que, además, puede contener un conjunto de animaciones. Se siente natural, ¿no?

Puede consultar otros ejemplos en esta biblioteca , como la forma en que se gestiona la asignación de memoria en forma de grano fino al hacer que las clases se hereden de variantes de una clase CRTP sobrecargar nuevo y eliminar.

Como se dijo, los principales problemas con la herencia múltiple se deben a la combinación de conceptos relacionados. Hace que el lenguaje tenga que establecer implementaciones complejas (vea la forma en que C ++ permite jugar con el problema de diamante ...) y el usuario no está seguro de lo que está sucediendo en esa implementación. Por ejemplo, lea este artículo que explica cómo se implementa en C ++ .

Eliminarla del lenguaje ayuda a evitar que las personas que no saben cómo se implementa el lenguaje para hacer las cosas mal. Pero obliga a pensar de una manera que, a veces, no se siente natural, incluso si se trata de casos extremos, es más frecuente que pueda pensar.

    
respondido por el Klaim 14.08.2011 - 14:08
4

Hay un concepto llamado mixins que se usa mucho en lenguajes más dinámicos. La herencia múltiple es una de las formas en que un idioma puede admitir mixins. Los mixins se usan generalmente como una forma para que una clase acumule diferentes piezas de funcionalidad. Sin la herencia múltiple, tiene que usar la agregación / delegación para obtener un comportamiento de tipo mixin con una clase, que es un poco más sintaxis pesada.

    
respondido por el RationalGeek 15.08.2011 - 14:10
2

Creo que la elección se basa principalmente en problemas debidos al problema del diamante .

Además, a menudo es posible eludir el uso de herencia múltiple por delegación u otros medios.

No estoy seguro del significado de tu última pregunta. Pero si es "¿en qué casos es útil la herencia múltiple?", Entonces en todos los casos en los que le gustaría tener un objeto A que tenga funcionalidades de los objetos B y C, básicamente.

    
respondido por el dagnelies 14.08.2011 - 16:55
2

No ahondaré mucho aquí, pero seguramente puedes entender la herencia múltiple en python a través del siguiente enlace enlace :

  

La única regla necesaria para explicar la semántica es la regla de resolución utilizada para las referencias de atributos de clase. Esto es primero en profundidad, de izquierda a derecha. Por lo tanto, si un atributo no se encuentra en DerivedClassName, se busca en Base1, luego (recursivamente) en las clases base de Base1, y solo si no se encuentra allí, se busca en Base2, y así sucesivamente.

...

  

Está claro que el uso indiscriminado de herencia múltiple es una pesadilla de mantenimiento, dada la dependencia de Python en las convenciones para evitar conflictos de nombres accidentales. Un problema bien conocido con herencia múltiple es una clase derivada de dos clases que tienen una clase base común. Si bien es bastante fácil averiguar qué sucede en este caso (la instancia tendrá una copia única de "variables de instancia" o atributos de datos utilizados por la clase base común), no está claro que estas semánticas sean de ninguna manera útil.

Esto es solo un pequeño párrafo pero lo suficientemente grande como para despejar las dudas que supongo.

    
respondido por el Pankaj Upadhyay 14.08.2011 - 14:39
1

Un lugar donde la herencia múltiple sería útil es una situación en la que una clase implementa varias interfaces, pero le gustaría tener alguna funcionalidad predeterminada integrada en cada interfaz. Esto es útil si la mayoría de las clases que implementan alguna interfaz quieren hacer algo de la misma manera, pero ocasionalmente necesitas hacer algo diferente. Puede tener cada clase con la misma implementación, pero tiene más sentido empujarla hacia arriba en una ubicación.

    
respondido por el KeithB 15.08.2011 - 13:49
1
  

¿Cuáles son los patrones de problemas típicos que se benefician de un código?   ¿Diseñado para hacer un uso intensivo de la herencia múltiple?

Este es solo un ejemplo, pero me parece inestimable para mejorar la seguridad y mitigar las tentaciones de aplicar cambios en cascada a través de las personas que llaman o las subclases.

Donde he encontrado que la herencia múltiple es increíblemente útil, incluso para las interfaces más abstractas y sin estado, es el lenguaje de interfaz no virtual (NVI) en C ++.

Ni siquiera son clases básicas abstractas , sino interfaces que solo tienen un poco de implementación para hacer cumplir los aspectos universales de sus contratos, como En realidad, no están reduciendo la generalidad del contrato sino mejor aplicándolo.

Ejemplo simple (algunos pueden verificar que un identificador de archivo pasado esté abierto o algo así):

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

En este caso, tal vez se llama a f por mil lugares en el código base, mientras que f_impl se anula por cien subclases.

Sería difícil realizar este tipo de verificación de seguridad en los 1000 lugares que llaman f o los 100 lugares que anulan f_impl .

Al hacer de este punto de entrada la funcionalidad no virtual, me da un lugar central para realizar esta comprobación. Y esta comprobación no está reduciendo la abstracción en lo más mínimo, ya que simplemente está afirmando una condición previa requerida para llamar a esta función. En cierto sentido, podría decirse que está fortaleciendo el contrato provisto por la interfaz y aliviando la carga de verificar el x de entrada para asegurarse de que cumple con las condiciones previas válidas en los 100 lugares que lo anulan.

Es algo que desearía que todos los idiomas hubieran tenido, y también hubiera deseado, incluso en C ++, que fuera un poco más de un concepto nativo (por ejemplo, no es necesario que definamos una función separada para anular).

Esto es extremadamente útil si no hiciste este assert por adelantado, y te diste cuenta que lo necesitabas más tarde cuando algunos lugares aleatorios en el código base encontraban valores negativos que se pasaban a f .

    
respondido por el user204677 06.01.2016 - 21:05
0

Primero: copias múltiples de la clase base (un problema de C ++) & acoplamiento apretado entre la base y las clases derivadas.

Segundo: herencia múltiple de interfaces abstractas

    
respondido por el quant_dev 14.08.2011 - 14:05

Lea otras preguntas en las etiquetas