40 OOP 2 – Costruttore
dato il seguente oggetto
let autorePreferito = { cognome: "Asimov", nome: "Isaac", "libro-preferito": { titolo: "L`uomo del bicentenario", pagine: 288 } }
da notare la proprietà libro-preferito che normalmente sarebbe un nome non accettato perché considerato un’espressione (differenza tra la variabile libro e la variabile preferito), ma che se racchiuso tra doppi o singoli apici viene accettato anche come nome. Non è comunque una forma consigliabile, sopratutto risulta scomodo il richiamo, infatti non è possibile farlo con la solita sintassi col punto o con le virgole
writeln(autorePreferito.libro-preferito.pagine); //scorretta writeln(autorePreferito."libro-preferito".pagine); //scorretta
bisogna usare la notazione di richiamo come per gli array, quindi tra parentesi quadre
writeln(autorePreferito["libro-preferito"].pagine);
altro caso interessante è quello di non incorporare nell’oggetto autorePreferito la struttura del libro, ma creare un oggetto esterno a parte libro e richiamare il suo riferimento. Nell’esempio creo l’oggetto libro e dentro l’oggetto autorePreferito creo la proprietà con il riferimento all’oggetto esterno libro
let libro = { titolo: "Fondazione";, pagine: 571 } let autorePreferito = { cognome: "Asimov", nome: "Isaac", "libro-preferito": { titolo: "L`uomo del bicentenario", pagine: 288 }, bestSeller: libro }
con questa property possiamo utilizzare la sintassi standard col punto
writeln(autorePreferito.bestSeller.titolo);
abbiamo parlato di riferimento all’oggetto libro esterno perchè si potrebbe pensare che la proprietà bestSeller: libro possa essere una copia dell’oggetto che viene messa in memoria solo nell’oggetto autorePreferito, invece non è così il riferimento è proprio l’indirizzo occupato in memoria dall’oggetto libro ed una modifica ad una sua proprietà si ripercuote sull’oggetto stesso. A prova di ciò stampiamo il valore della proprietà pagine di libro, modificando prima il valore della proprietà pagine di bestSeller modificando:
autorePreferito.bestSeller.pagine=1000; writeln(libro.pagine);
Come si noterà il valore stampato di libro.pagine è uguale a 1000, la proprietà dell’oggetto esterno libro è stata modificata. Quando si ci riferisce a questa copia dell’indirizzo di riferimento di un oggetto, si parla di copy shallow (copia superficiale), Bisogna fare molta attenzione a lavorare con la shallow copy degli oggetti, pur essendoci il vantaggio in termini di risparmio di risorse, perchè si potrebbero modificare oggetti in uso esternamente, pensando invece di lavorare all’interno di un altro oggetto. Lavorare invece con la deep copy (copia profonda) che permette di clonare completamente un oggetto e le sue caratteristiche in un altro, non è una cosa banale, ci viene in aiuto la libreria jQuery che con la funzione extend duplica l’intero oggetto:
bestSeller2: jQuery.extend(true, {}, libro)
Ovviamente bisogna scaricare jQuery nella stessa cartella del progetto ed includerlo nel file
<script type="text/javascript" src="jquery-3.1.1.min.js"> </script>
analizzando il metodo extend
extend(true, {}, libro)
scopriamo che il primo parametro se è impostato a true esegue una copia deep, andando a prendere l’oggetto e ricreando perfettamente la sua intera struttura. Il secondo parametro è il nome dell’oggetto nel quale effettuare la copia, si potrebbe anche indicare con this.bestSeller2 al posto delle graffe, anche se viene comoda la sintassi con le graffe {} che permette di creare al volo un oggetto vuoto il cui riferimento verrà poi restituito da extend a besSeller2. Il terzo parametro è l’oggetto da duplicare.
Cambiando adesso il valore di pagine
autorePreferito.bestSeller2.pagine=9999;
scopriamo che l’oggetto originale non ha subito cambiamenti
writeln(libro.pagine); writeln(autorePreferito.bestSeller2.pagine);
Col primo comando otterremo il valore originale di 571 e col secondo 9999.
Questo tipo di approccio in stile procedurale, con deep o shallow copy, cambio di valori al volo in stile dinamico come con le funzioni, creare regole o logiche di protezione, non è particolarmente indicato, soprattutto per il fatto che la OOP prevede il concetto di metodo costruttore,
Grazie al metodo costruttore possiamo procedere alla creazione di nuovi oggetti di tipo autore come fosse una classe, il cui nome è proprio quello della funzione costruttore (nel nostro caso Autore)
autorePreferito = new Autore("Isaac", "Asimov", "L`uomo del bicentenario", 288, libro);
Si è scelto di far passere come parametri quelli dell’autore (nome, cognome), che quelli che descrivono il libro preferito (titolo, pagine), mentre per il bestSeller l’intero oggetto libro che verrà clonato sempre con l’aiuto di jQuery .
Ecco la funzione costruttore Autore :
function Autore(cognome, nome, titolo, pagine, bestSeller) { //stato interno this.cognome = cognome; this.nome = nome; this.libroPreferito = {titolo,pagine}; this.bestSeller = jQuery.extend(true, {}, bestSeller); }
é sufficiente assegnare i valori dei parametri ricevuti alle variabili dello stato interno dell’oggetto autore utilizzando il this come già visto. ES6 accetta una sintassi più snella nella creazione degli oggetti nella forma letterale, in quanto nell’assegnamento this.libroPreferito = {titolo,pagine}; mancherebbero le chiavi (coppia chiave valore) come indicato di seguito
this.libroPreferito = {titolo: titolo, pagine: pagine};
La forma abbreviata è preferibile, perchè più snella e chiara.
Facciamo una prova di stampa:
writeln(autorePreferito.libroPreferito.titolo); writeln(autorePreferito.libroPreferito.pagine); autorePreferito.bestSeller.pagine=777; writeln(libro.pagine);
e notiamo che nuovamente la modifica alle pagine del bestSeller interno che corrisponde al libro indicato nel costruttore, mantiene la sua autonomia, quindi nonostante imposti la property pagine a 777 stampando writeln(libro.pagine); ottengo 571.
Ecco il codice nuovo completo per una maggiore chiarezza:
let libro = { titolo: "Fondazione", pagine: 571 } function Autore(cognome, nome, titolo, pagine, bestSeller) //costruttore { //stato interno this.cognome = cognome; this.nome = nome; this.libroPreferito = {titolo,pagine}; this.bestSeller = jQuery.extend(true, {}, bestSeller); } autorePreferito = new Autore("Isaac", "Asimov", "L`uomo del bicentenario", 288, libro); writeln(autorePreferito.libroPreferito.titolo); writeln(autorePreferito.libroPreferito.pagine); autorePreferito.bestSeller.pagine=777; writeln(libro.pagine);
altrettanto semplice è aggiungere metodi al costruttore, assegnando delle funzioni anonime alla solita proprietà interna che diventerà il metodo da richiamare all’esterno
this.getSchedaBestSeller = function(){...}
oppure con un nome qualora si necessiti della ricorsione:
this.metodo = function pippo(){ ... pippo(...) }
nell’esempio si è preparato una cornice al titolo
this.getSchedaBestSeller = function() { let stringa = this.bestSeller.titolo + " - " + this.bestSeller.pagine; let stringaConCornicetta = "*".repeat( stringa.length + 1 ) + "<br>" + "* " + stringa + " *" + "<br>" + "*".repeat( stringa.length + 1 ); return stringaConCornicetta; }
Memorizzo il titolo e le pagine dentro stringa, stampo * tante volte quanto è lunga (lenght) stringa con la funzione repeat, a capo stampo stringa e infine ripeto la stampa degli *.
Lancio il metodo
writeln(autorePreferito.getSchedaBestSeller() );
uguale per la scheda autore incorniciata:
this.getSchedaAutore = function() { let stringa = this.cognome + " - " + this.nome; let stringaConCornicetta = "*".repeat( stringa.length + 1 ) + "<br>" + "* " + stringa + " *" + "<br>" + "*".repeat( stringa.length + 1 ); return stringaConCornicetta; }
Con il costruttore è facile costruire collezioni di tipo autore, definiamo un array schedario
let schedario = [ new Autore("n1","c1", "t1", Math.random(100) + 1, {}), new Autore("n2","c2", "t2", Math.random(100) + 1, {}) ];
non abbiamo passato il bestSeller, quindi compiliamo l’ultimo parametro con un oggetto vuoto {},
Gli altri dati sono solo da esempio messi a casaccio, aggiungo un terzo autore
schedario.push( new Autore("n3","c3", "t3", Math.random(100) + 1, {}) );
e con un ciclo for comando la stampa della scheda autore incorniciata:
for (let autore of schedario) { writeln( autore.getSchedaAutore() ); }
Ecco il codice completo:
let libro = { titolo: "Fondazione", pagine: 571 } let schedario = [ new Autore("n1","c1", "t1", Math.random(100) + 1, {}), new Autore("n2","c2", "t2", Math.random(100) + 1, {}) ]; schedario.push( new Autore("n3","c3", "t3", Math.random(100) + 1, {}) ); for (let autore of schedario) { writeln( autore.getSchedaAutore() ); } function Autore(cognome, nome, titolo, pagine, bestSeller) //costruttore { //stato interno this.cognome = cognome; this.nome = nome; this.libroPreferito = {titolo,pagine}; this.bestSeller = jQuery.extend(true, {}, bestSeller); //metodi this.getSchedaBestSeller = function() { let stringa = this.bestSeller.titolo + " - " + this.bestSeller.pagine; let stringaConCornicetta = "*".repeat( stringa.length + 1 ) + "<br>" + "* " + stringa + " *" + "<br>" + "*".repeat( stringa.length + 1 ); return stringaConCornicetta; } this.getSchedaAutore = function() { let stringa = this.cognome + " - " + this.nome; let stringaConCornicetta = "*".repeat( stringa.length + 1 ) + "<br>" + "* " + stringa + " *" + "<br>" + "*".repeat( stringa.length + 1 ); return stringaConCornicetta; } } autorePreferito = new Autore("Isaac", "Asimov", "L`uomo del bicentenario", 288, libro); writeln(autorePreferito.libroPreferito.titolo); writeln(autorePreferito.libroPreferito.pagine); autorePreferito.bestSeller.pagine=777; writeln(libro.pagine); writeln(autorePreferito.getSchedaBestSeller() );