Ereditarietà multipla

caratteristica di alcuni linguaggi di programmazione

Alcuni linguaggi di programmazione permettono di utilizzare l'ereditarietà multipla, in cui una classe può ereditare funzionalità e caratteristiche da più di una classe base. Questa tecnica si contrappone all'ereditarietà singola, in cui una classe può ereditare da una, e solo una, classe base.

Nella programmazione orientata agli oggetti, l'ereditarietà descrive una relazione gerarchica fra due classi, nella quale una (quella che eredita) è chiamata "classe derivata" (o "classe figlia", o "sottotipo"), e l'altra (quella da cui si eredita) è chiamata "classe genitrice" (o "classe base", o "superclasse"). Le funzionalità ereditate dalla "figlia" permettono agli oggetti istanziati su di essa di condividere le funzionalità della "genitrice", cioè della classe base.

Per esempio: supponiamo di aver creato una classe:

Mammifero dotata di funzionalità come mangiare, respirare, riprodursi, ecc. Possiamo definire un'altra classe:

Topo che eredita tutte le funzionalità di Mammifero senza doverle ridefinire, in quanto, essendo il topo un mammifero, ne condivide tutte le caratteristiche. A queste funzionalità la nuova classe ne aggiunge altre, possedute dai "topi" e non comuni a tutti i mammiferi, come, ad esempio raccogliere spazzatura. Oltre che avere le caratteristiche comuni a tutti i mammiferi, tuttavia, i topi condividono altre caratteristiche e funzionalità con altre "classi" di oggetti, come, ad esempio:

Personaggi di Ratatouille oppure Vettori di malattie mortali Quindi è molto comodo, per la classe Topo, poter ereditare anche le caratteristiche e le funzionalità di queste classi, senza doverle ridefinire.

Problema del diamante

modifica
 
Diagramma del "problema del diamante"

L'ereditarietà multipla può essere causa, in alcuni contesti, di qualche confusione, tanto che alcuni ritengono che gli inconvenienti siano maggiori dei benefici.

Una possibile causa di ambiguità è la seguente: se due classi B e C ereditano dalla classe A e la classe D eredita sia da B che da C, se un metodo in D chiama un metodo definito in A, da quale classe viene ereditato?

Tale ambiguità prende il nome di problema del diamante (in inglese diamond problem), a causa della forma del diagramma di ereditarietà delle classi, simile ad un diamante.

Differenti linguaggi di programmazione hanno risolto quest'inconveniente in modi diversi.

Implementazione in alcuni linguaggi

modifica

Java e .NET

modifica

In Java si è adottato questo compromesso: una classe può ereditare le interfacce da più di una classe base – cioè esporre all'esterno gli stessi metodi delle interfacce delle classi base – ma può ereditare i dati ed i metodi effettivi da una sola classe base. Anche i linguaggi della piattaforma Microsoft .NET, come C# e Visual Basic utilizzano lo stesso approccio.

Dalla versione Java 8, tuttavia, si sono introdotti i modificatori "default" che se associati a metodi di interfacce permettono (al contrario dei metodi senza questo modificatore) non solo di dichiarare ma anche di implementare in modo differente gli stessi metodi nelle interfacce. È comunque d'obbligo l'override dei metodi nelle sottoclassi che implementano entrambe le interfacce, ma tramite un'istruzione ben definita si può invocare un preciso metodo implementato di una specifica interfaccia genitrice. Così nel caso del problema del diamante la classe D effettua l'override del metodo in comune a B e C definito in A e invoca lo specifico metodo di B o C.

C++ per default segue ogni percorso di ereditarietà separatamente, quindi l'oggetto D contiene due separati oggetti di A e gli usi dei membri di A devono essere opportunamente qualificati. Se l'ereditarietà da A a B e l'ereditarietà da A a C sono entrambe segnate come "virtual" ("class B : virtual A"), C++ si prende particolare cura di creare solo un oggetto A, l'utilizzo dei membri di A funziona correttamente. Se l'ereditarietà virtuale e l'ereditarietà non virtuale sono mischiate, esiste solo un A virtuale e un A non virtuale per ogni percorso di ereditarietà non virtuale a A.

In Eiffel le classi derivate adattano le funzionalità ereditate rinominandole o definendo anticipatamente le regole per risolvere le ambiguità e decidere quale versione applicare (quella ereditata o quella della classe base).

REALbasic

modifica

Analogamente a Java anche in REALbasic si possono ereditare metodi e dati da una sola classe base, e, in più, sono previsti metodi aggiuntivi per "estendere" le funzionalità di una classe senza dover ricorrere all'ereditarietà.

In Perl le classi da cui ereditare sono disposte in una lista ordinata, e viene poi chiamato il primo metodo trovato in ordine gerarchico di profondità della prima classe della lista, e così di seguito per le classi successive e per le loro rispettive classi base.

Python supporta una forma di ereditarietà multipla, e viene chiamato un metodo attraverso la regola prima-in-profondità, da-sinistra-a-destra. Perciò, se un attributo non viene trovato nella classe derivata, viene cercato nella prima classe base, poi (ricorsivamente) nelle classi base di quest'ultima e, solo se non vi è stato trovato, viene ricercato nella seconda classe base della classe derivata, e così via.

Esempio

modifica

Ecco un esempio di ereditarietà multipla in C++:

class A
{
 protected:
  int i;
 public:
  int get_i() {return i;}
  void set_i(int n) {i=n;}
};

class B
{
 protected:
  int j;
 public:
  int get_j() {return j;}
  void set_j(int n) {j=n;}
};

class C : public A, public B
{
 protected:
  int k;
 public:
  int get_k() {return k;}
  void set_k(int n) {k=n;}
};

In questo caso, C erediterà sia da A che da B, ed avrà quindi le variabili d'istanza i e j e le funzioni get_i, set_i, get_j e set_j, oltre che a k ed a get_k ed a set_k.

Voci correlate

modifica

Collegamenti esterni

modifica
  Portale Informatica: accedi alle voci di Wikipedia che trattano di Informatica