What are the typical problem patterns that benefit from a code
designed to make heavy use of multiple inheritance?
Questo è solo un esempio, ma ne trovo di valore inestimabile per migliorare la sicurezza e attenuare le tentazioni per applicare le modifiche a cascata in tutti i chiamanti o sottoclassi.
Dove ho trovato l'ereditarietà multipla incredibilmente utile anche per le interfacce più astratte e senza stato è l'idioma di interfaccia non virtuale (NVI) in C ++.
Non sono nemmeno veramente classi base astratte così come le interfacce che hanno solo un po 'di implementazione per far rispettare gli aspetti universali dei loro contratti, non stanno davvero restringendo la generalità del contratto tanto quanto meglio rafforzandolo.
Semplice esempio (alcuni potrebbero verificare che un handle di file passato sia aperto o qualcosa del genere):
// 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;
};
In questo caso, forse f
è chiamato da migliaia di posti nella base di codice, mentre f_impl
è sovrascritto da un centinaio di sottoclassi.
Sarebbe difficile eseguire questo tipo di controllo di sicurezza in tutti i 1000 luoghi che chiamano f
o tutti i 100 posti che sovrascrivono f_impl
.
Semplicemente rendendo questo punto di accesso alla funzionalità non virtuale, mi dà un posto centrale per eseguire questo controllo. E questo controllo non riduce l'astrazione minimamente, poiché sta semplicemente affermando una precondizione richiesta per chiamare questa funzione. In un certo senso, sta probabilmente rafforzando il contratto fornito dall'interfaccia e alleggerendo l'onere di verificare l'input x
per assicurarsi che sia conforme alle precondizioni valide in tutti i 100 luoghi che lo sovrascrivono.
È qualcosa che vorrei che ogni linguaggio avesse, e anche desiderato, anche in C ++, che fosse un po 'più di un concetto nativo (es: non ci richiede di definire una funzione separata per l'override).
Questo è estremamente utile se non hai fatto questo assert
in anticipo, e ti sei reso conto che ne avevi bisogno in seguito, quando alcuni posti casuali nella base di codice stavano incontrando valori negativi passati a f
.