¿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 invaluable 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 idioma de interfaz no virtual (NVI) en C ++.
Ni siquiera son clases base realmente abstractas, sino interfaces que solo tienen un poco de implementación para hacer cumplir los aspectos universales de sus contratos, ya que en realidad no están reduciendo la generalidad del contrato, sino más bien haciéndolo cumplir mejor .
Ejemplo simple (algunos podrían comprobar 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, quizás f
sea llamado por mil lugares en la base de código, mientras que f_impl
es anulado por cien subclases.
Sería difícil hacer este tipo de verificación de seguridad en los 1000 lugares que llaman f
o en los 100 lugares que anulan f_impl
.
Al hacer que este punto de entrada a la funcionalidad no sea virtual, me da un lugar central para realizar esta verificación. Y esta verificació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 fortalece el contrato proporcionado por la interfaz y alivia la carga de verificar la x
entrada para asegurarse de que se ajuste a las condiciones previas válidas en los 100 lugares que la anulan.
Es algo que desearía que todos los lenguajes tuvieran, y también deseaba, incluso en C ++, que fuera un concepto un poco más nativo (por ejemplo: no nos obliga a definir una función separada para anular).
Esto es extremadamente útil si no lo hizo assert
por adelantado, y se dio cuenta de que lo necesitaba más tarde cuando algunos lugares aleatorios en la base de código encontraban valores negativos que se les pasaban f
.