¿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
.