Ereditarietà, usarla con cautela

Ereditarietà, usarla con cautela

In questo post voglio condividere alcune osservazioni sulle caratteristiche della relazione di ereditarietà nella programmazione object-oriented. Provo a farlo con un esempio.

Dobbiamo individuare il modello delle classi per una applicazione che gestisca l’archivio dei film di una videoteca. Per ciascun film dobbiamo memorizzare il regista e gli attori. Viene immediato individuare almeno tre classi: Film, Regista e Attore, collegate nel seguente modo:

Diagramma 1

Poiché dobbiamo realizzare delle funzionalità che operano sia sugli attori che sui registi, come ad esempio il motore di ricerca degli artisti, abbiamo bisogno del polimorfismo e quindi introduciamo una classe Artista. Infatti Attore IS-A Artista e anche Regista IS-A Artista, quindi possiamo modellare queste due relazioni con l’ereditarietà.

Diagramma 2

Tutto corretto vero? Si, fino a quando ci tocca censire un film di Woody Allen in cui il regista è anche un attore del film. Quindi Woody Allen è certamente un Artista, ma è contemporaneamente sia Regista che Attore. Siamo costretti quindi ad avere due oggetti, uno della classe Attore e uno della classe Regista, che in realtà fanno riferimento allo stesso soggetto. Questa duplicazione prima o poi produrrà delle anomalie. Infatti, se non sviluppassimo specifici workaround, Woody Allen comparirebbe due volte tra i risultati del motore di ricerca degli artisti.

Entità vs ruoli

Che errore abbiamo commesso formulando il precedente modello? Abbiamo introdotto la relazione di ereditarietà per via della veridicità delle seguenti affermazioni:

  • Attore è un Artista;
  • Regista è un Artista;

ma abbiamo trascurato le seguenti implicazioni che l’ereditarietà comporta:

  • un Artista può essere o un Attore o un Regista;
  • un Artista di tipo Regista non può essere anche di tipo Attore, e viceversa;

ma queste conseguenze dell’ereditarietà non sono compatibili con le dinamiche dell’applicazione che stiamo progettando, come dimostrato dall’istanza “Woody Allen”.

Probabilmente a trarci in inganno è il modo utilizzato, nella lingua italiana, per indicare il ruolo ricoperto da un soggetto. Infatti diciamo che “il sig. Rossi è un impiegato” ma intendiamo dire “il sig. Rossi ha un impiego presso…”. E quando il sig. Rossi andrà in pensione viene spontaneo dire che “il sig. Rossi è un pensionato”, intendendo in realtà dire che “il sig. Rossi ha una pensione”. Abbiamo quindi la tendenza ad utilizzare il verbo “è un” con il significato di “ha un”, e di conseguenza commettiamo l’errore di utilizzare la relazione IS-A (l’ereditarietà) al posto della relazione HAS-A (associazione).

Il modello che abbiamo inizialmente pensato va evidentemente rivisto, ad esempio nel seguente modo: introduciamo una classe Ruolo, padre di Attore e Regista. Associamo quindi la classe Ruolo alla classe Artista. In questo modo un artista non è costretto ad essere alternativamente o un Attore o un Regista, bensì può avere il ruolo di attore e/o di regista.

Diagramma 3

In questo modo per Woody Allen avremo una sola istanza di Artista, associata a due istanze di Ruolo, una di tipo Attore e una di tipo Regista.

Conclusioni

Come abbiamo visto nell’esempio, la relazione di ereditarietà impone forti vincoli al modello, ben più di quelli che il verbo “IS-A” ci suggerisce. Inoltre il verbo “IS-A” della relazione di ereditarietà non va confuso con l’affermazione “è un” della lingua italiana, spesso utilizzata per indicare una caratteristica, un ruolo.

Considerati i vincoli che impone, risulta quindi opportuno introdurre una relazione di ereditarietà in un modello ad oggetti solo quando sappiamo come sfruttarla, ad esempio tramite il polimorfismo, e non semplicemente perché risulta vera nel mondo reale, o peggio nella lingua italiana.