41 OOP 3 – Eriditarietà
L’eridatarietà vuole collegare concetti tra loro collegati secondo una gerarchia.
La relazione tra un nodo genitore e i suoi figli si dice is-a (è un), il pensionato È UN tipo di persona, così i disoccupati, il dipendente È UN tipo di lavoratore, e così via.
Entrando nel vivo della nostra programmazione, possiamo identificare nell’oggetto Persone tutte quelle proprietà (e metodi) che potrei ritrovarmi condivise negli oggetti da cui discendono, qualora le implementassi separatamente. Scendendo più in basso troverei una specializzazione delle caratteristiche, mantenendo anche le proprietà genitori. Eviterei una ripetizione del codice, aggiungendo alcune proprietà o effettuando delle sfumature ai tipi di metodo delle classi antenate, entra in gioco così il concetto di mascheramento o shadowing . L’oggetto Dipendenti non si accontenta di ciò che è standard nell’oggetto Lavoratori, ma va ad aggiungere caratteristiche tipiche. Questa notevole caratteristica di ereditarietà, ha lo svantaggio che ogni modifica effettuata negli oggetti genitori si ripercuote anche in tutti gli oggetti discendenti, percui è necessaria un’adeguata progettazione in modo da non dover incombere in eventuali problemi dovuti a eventuali cambiamenti futuri. Una corrente di pensiero molto forte sostiene che è buona norma invece di estendere un oggetto genitore, è più conveniente integrarlo, magari con un collegamento nell’ oggetto figlio, rendendolo maggiormente indipendente. Se nei linguaggi tradizionali come C++, Java, C# il concetto di ereditarietà è garantito da un modello a classi ben delineato, in js ha un approccio di completa libertà nell’implementazione che porta notevole responsabilità al programmatore. Questo corrisponde ad avere dei meccanismi sintattici di base che ci permettono di definire il nostro concetto di classe ed ereditarietà. Volendo fare un paragone automobilistico, da una parte abbiamo una bellissima automobile pronta all’uso ma senza poter fare modifiche, dall’altra abbiamo l’officina che ci permette di costruire la macchina che vogliamo.
Alla base di questo concetto, in js è importante comprendere il concetto dei prototipi o prototype .
Nonostante ES6 abbia introdotto delle novità sintattiche per le classi, alla base bisogna sempre lavorare col modello (o prototipo), entriamo nel pratico:
devo creare un oggetto Fantozzi che discende da un oggetto persona, cerchiamo una caratteristica comune a TUTTE le persone: il nome
let persona = { nome: ""};
persona ha una proprietà nome che sarà comune ad ogni oggetto discendente. ogni persona ha un nome, che sia un megadirettore galattico o che sia il rag. Ugo.
Creiamo quindi l’oggetto fantozzi basato sul modello persona
let fantozzi = Object.create( persona );
Viene sfruttato l’oggetto predefinito Object che è il padre di tutti gli oggetti del js e usato il suo metodo create che crea un nuovo oggetto fantozzi basandosi come modello sull’oggetto persona. Fantozzi si ritrova automatico la proprietà nome intrinseca..
Per richiamare la proprietà nome di fantozzi e assegnarle un valore, devo usare il metodo getPrototypeOf di Object
Object.getPrototypeOf(fantozzi).nome = "ugo";
Scrivendo Object.getPrototypeOf(fantozzi).nome equivale a scrivere persona.nome, infatti con il seguente alert l’espressione mi restituisce TRUE
alert(persona===Object.getPrototypeOf(fantozzi));
La stampa fortunatamente è nettamente più snella
writeln( fantozzi.nome );
Il motore js va a cercare localmente nell’oggetto fantozzi la proprietà nome, non trovandola, automaticamente guarda se esiste un modello di riferimento, lo trova in persona, a quel punto verifica la proprietà nome in persona e infine la stampa. Questa ricerca a ritroso nella gerarchia viene chiamata prototype chaining o catena dei prototipi . Se avessimo anche 1000 prototipi discendenti, la ricerca andrebbe a ritroso fino al ritrovamento della proprietà e fino all’oggetto Object, qualora non esista in nessuno, viene restituito undefined.
Se per qualche motivo creo la stessa proprietà assegnando un nuovo valore a fantozzi
fantozzi.nome = "il super sfigato";
js mi restituirà subito questo valore interrompendo la ricerca nel prototype chaining.
Non abbiamo però eliminato il valore Ugo, lo abbiamo solo mascherato (shadowing), usando il metodo getPrototypeOf possiamo richiamarlo
writeln( Object.getPrototypeOf(fantozzi).nome );
Bisogna fare attenzione, a differenza degli altri linguaggi, al fatto che se creassi un nuovo oggetto col prototipo persona, non avrebbe la sua copia distinta, ma condividerebbe il prototipo persona con fantozzi.