In questo quarto post dedicato al mondo Java, vedremo il secondo paradigma della programmazione ad oggetti: l’ereditarietà.
L’ereditarietà un concetto molto famigliare a noi. Vediamo un semplice esempio a riguardo.
Facciamo l’esempio che dobbiamo realizzare un programmare che permette di gestire alcuni dati relativi a persone, in particolare a studente. Una persona può avere i seguenti attributi:
- Nome
- Cognome
- Data di nascita
- Città di nascita
Un metodo che può essere usato in alcuni applicazioni, può essere ad esempio:
- calcolaEtà()
Uno studente, può avere i seguenti attributi:
- Nome
- Cognome
- Data di nascita
- Città di nascita
- Matricola
- Media dei Voti
Come metodi può avere:
- calcolaEtà()
- mostraMediaVoti()
Non notate qualcosa che accomuna queste due “classi” ? Sicuramente ci sono quattro attributi in comune e il metodo in comune è quello che permette di calcolare l’età. Qualora dovessimo scrivere queste due classi separatamente, dovremmo scrivere del “codice duplicato” e questo non va bene, perché eventuali modifiche, richiederebbero di andare alla ricerca del codice in più punti diversi del programma e quando i progetti diventano sempre più grandi, diventa complicata questa procedura !
Dal punto di vista “operativo” l’ereditarietà si implementa con la seguente sintassi:
nomeClasseFiglia extends nomeClassePadre
Occorre notare che in Java non è supportata l’ereditarietà multipla (cosa possibile in C++). Questa scelta è stata fatta ancora una volta per aumentare la robustezza del linguaggio di casa Oracle.
Un esempio relativo a questo programma è possibile trovarlo nella seguente cartella.
L’unico metodo che richiede un ulteriore commento riguarda quello del calcolo dell’età:
public int getAge(Date date){ if((date.getMonth()>=birth.getMonth() && date.getDay()>=birth.getDay()) || date.getMonth()>birth.getMonth()){ return date.getYear()-birth.getYear(); }else{ return date.getYear()-birth.getYear()-1; } }
Il metodo accetta come parametro una data, che può essere quella di oggi e restituisce un intero, che rappresenta l’età della persona. L’if in particolare non è immediato da capire. Facciamo un esempio:
oggi è il 14 di Dicembre e prendiamo la mia data di nascita 16 Luglio 1992. Attraverso il primo if, verifico se sia il mese che il giorno sia maggiore della data di nascita, mentre nella seconda condizione, si verifica che il mese della data passata come parametro, sia maggiore del mese di nascita. Se non ci fosse la seconda condizione, io non avrei ancora compiuto gli anni, perché 14>16 !
Ora introduciamo un nuovo tool importante per la programmazione ad oggetti: i diagrammi UML
Questi diagrammi permettono di capire subito le relazioni che ci sono tra le varie classi, i metodi e gli attributi di ciascuna di esse.
Facciamo ancora un altro esempio sull’ereditarietà. Facciamo l’ipotesi che stiamo lavorando su un programma di geometria, che richiede l’utilizzo della classi rettangolo e triangolo, per il calcolo delle aree. Potremmo erroneamente, applicare così l’ereditarietà:
public class Triangolo { public final int NUMERO_LATI = 3; public float lunghezzaLatoUno; public float lunghezzaLatoDue; public float lunghezzaLatoTre; } public class Rettangolo extends Triangolo { public final int NUMERO_LATI = 4; public float lunghezzaLatoQuattro; ........ }
Questo codice è corretto ? Per rispondere alla domanda in generale, cioè capire se l’ereditarietà di giusto oppure no, occorre porsi la seguente domanda:
nomeClasseFiglia è un nomeClassePadre ?
In quest’ultimo caso:
un rettangolo è un triangolo ? La risposta è no ovviamente !
Vediamo un po’ come poter ristrutturare il programma. Come primo passo, è necessario usare un concetto importantissimo in Java che prende in nome di astrazione e successivamente di generalizzazione.
Con il primo termine, si intende di “l’arte di sapersi concentrare solo sui dettagli veramente essenziali nella descrizione di un’entità”. Mente come generalizzazione, intendiamo il meccanismo con cui due classi con alcune caratteristiche in comuni, possano essere rappresentate da una classe Padre. Il codice dell’esempio relativo ai rettangolo e ai triangolo non è sicuramente uno dei migliori, ma serve come esempio per capire quando l’ereditarietà è utile da usare oppure no. Ecco il diagramma UML del progetto:
Conclusione
Perché usare l’ereditarietà ? L’ereditarietà serve per rendere il codice più semplice da leggere, più veloce da modificare e sopratutto per EVITARE CODICE DUPLICATO !