47 oop8 – classi4
Riprendendo il codice visto la precedente lezione, non ci sarebbe stato alcun beneficio nel creare PuntoIQColorato come clone identico di PuntoIQ, avremmo utilizzato direttamente quello.
Andiamo quindi ad aggiungere le personalizzazioni di PuntoIQColorato dichiarando un suo costruttore per aggiungere il colore utilizzando la nuova variabile colore
class PuntoIQColorato extends PuntoIQ { constructor (x, y, colore) { } }
Affrontiamo la seguente interessante osservazione: visto che i controlli sulle coordinate x, y per esempio del tipo, controlli sul risultato e sul catturare gli errori, sono già effettuati nella classe madre, sarebbe poco logico ripeterli anche su quella discendente, pertanto dichiarerò tramite la parola riservata super (super class), quali sono le proprietà che saranno considerate dal costruttore della classe madre.
class PuntoIQColorato extends PuntoIQ { constructor (x, y, colore) { super(x, y); } }
Quindi il costruttore di PuntoIQColorato e di tutti i suoi eventuali discendenti andrà prima ad utilizzare il il contenuto del costruttore di PuntoIQ per le variabili x ed y.
Aggiungo la proprietà tipica colore nel costruttore
class PuntoIQColorato extends PuntoIQ { constructor (x, y, colore) { super(x, y); this._colore = colore; } }
anche se non vietato, potrei andare a modificare direttamente la proprietà di _x dal costruttore della classe figlia
class PuntoIQColorato extends PuntoIQ { constructor (x, y, colore) { super(x, y); this._colore = colore; this._x = 999; //PERICOLOSO! } }
La cosa è altamente sconsigliata, perché starei andando a violare i segreti implementativi della classe da cui deriva. Pensate se la logica di _x prevedesse dei controlli di sicurezza o altre azioni collegate, in questo modo le staremmo bypassando tutte. Quindi come regola generale: è sempre meglio manipolare le variabili della classe madre attraverso i metodi che essa mette a disposizione.
Un altra osservazione è che se vado ad invertire le 2 righe delle proprietà mettendo prima this._colore = colore; otterrei un errore, in quanto il costruttore incontrando il this.colore, non avrebbe ancora sistemato i parametri precedenti x e y col super
constructor (x, y, colore) { this._colore = colore; //SEQUENZA ERRATA super(x, y); // QUESTA RIGA VA PRIMA }
Come buona abitudine: in ogni costruttore che eredita proprietà dal costruttore della classe madre, la prima istruzione da eseguire è super.
Andiamo a specializzare maggiormente la classe PuntoIQColorato, modificando il metodo esistente nella classe madre distanza(). Vogliamo che ci restituisca invece che la distanza geometrica, per esempio la distanza di codice colore tra i 2 punti. Il metodo modificato della classe discendente avrebbe la priorità su quello della classe madre, come prova facilitate restituiamo un valore
distanza( altro ) { return 1000; }
Il metodo locale della classe figlia, ha oscurato, o meglio ha effettuato shadowing su quello della classe madre avente stesso nome.
Se si vuole invece estendere il metodo della classe madre, ereditando le caratteristiche, si fa sempre ricorso all’uso del super
distanza( altro ) { return super.distanza(altro) + 1000; }
Il metodo distanza della classe figlia parte da quello della classe madre e aggiunge delle personalizzazioni.
Facciamo tutte le prove con le varie stampe, ecco il codice completo.
class PuntoIQ { constructor(x,y) { if (x<0 || y<0) throw "Non nel primo quadrante"; this._x =x; this._y=y;} get x() {return this._x;} set x(valore) { if ( typeof(valore) === "boolean" || isNaN(valore) ) throw "Non è un numero"; if(valore<0) throw "Non nel primo quadrante"; this._x = valore; } get y() {return this._y;} //calcola la distanza tra questo punto ed un secondo passato come parametro distanza(altro) { if (altro instanceof PuntoIQ) return Math.sqrt( Math.pow(this.x - altro.x, 2) + Math.pow(this.y - altro.y, 2)); else throw "Il parametro attuale `altro` non è un punto"; } } try { let p = new PuntoIQ(10,30); } catch (eccezione) { alert(eccezione); } class PuntoIQColorato extends PuntoIQ { constructor (x, y, colore) { super(x, y); this._colore = colore; //this._x = 999; PERICOLOSO! } distanza( altro ) { return super.distanza(altro) + 1000; } } let p1 = new PuntoIQ(5,5); let p2 = new PuntoIQ(10,10); writeln( p1.distanza(p2 ) ); //un punto colorato in partenza ha tutte le caratteristiche (variabili e metodi)di un punto `normale` let pc = new PuntoIQColorato(100,100); writeln("Punto Colorato: " + pc.x); writeln( pc.distanza(p2) );