2 - Unità didattica 7.3.b)
- Passo 3:
Notiamo che questo metodo ereditato nella
sottoclasse Punto3D ha bisogno di un override per avere un senso. Ma
ecco che le precedenti violazioni della regola dell’astrazione,
iniziano a mostrarci le prime incoerenze. Infatti, il metodo
dammiDistanza nella classe Punto3D, dovendo calcolare la distanza
tra due punti tridimensionali, dovrebbe prendere in input come
parametro un oggetto di tipo Punto3D, nella maniera seguente:
class Punto3D extends Punto
{
private int z;
. . . //inutile riscrivere l’intera classe
public double dammiDistanza(Punto3D p)
{
int tmp1=(x-p.x)*(x-p.x);
//quadrato della differenza della x dei due punti
int tmp2=(y-p.y)*(y-p.y);
//quadrato della differenza della y dei due punti
int tmp3=(z-p.z)*(z-p.z);
//quadrato della differenza della z dei due punti
return Math.sqrt(tmp1+tpm2+tpm3);
//radice quadrata della somma dei tre quadrati
}
}
in questa situazione però, ci troveremmo di
fronte ad un overload piuttosto che ad un override. Infatti, oltre
al metodo dammiDistanza(Punto3D p) sussiste in questa classe anche
il metodo dammiDistanza(Punto p) ereditato dalla classe Punto.
Quest’ultimo tuttavia, come abbiamo notato in precedenza, non
dovrebbe sussistere in questa classe, giacchè rappresenterebbe la
possibilità di calcolare la distanza tra un punto bidimensionale ed
uno tridimensionale.
La soluzione migliore sembra allora quella di "forzare" un override.
Possiamo infatti comunque riscrivere il metodo dammiDistanza(Punto
p). Dobbiamo però considerare il reference p come parametro
polimorfo, ed utilizzare il casting di oggetti all’interno del
blocco di codice, per garantire il corretto funzionamento del
metodo:
class Punto3D extends Punto
{
private int z;
. . . //inutile riscrivere l’intera classe
public double dammiDistanza(Punto p)
{
if (p instanceof Punto3D)
{
Punto3D p1=(Punto3D)p; //Casting
int tmp1 =
(getX()-p1.getX())*(getX()-p1.getX());
//quadrato della differenza della x dei due punti
int tmp2 =
(getY()-p1.getY())*(getY()-p1. getY());
//quadrato della differenza della y dei due punti
int tmp3=(z-p1.z)*(z-p1.z);
//quadrato della differenza della z dei due punti
return Math.sqrt(tmp1+tpm2+tpm3);
//radice quadrata della somma dei tre quadrati
}
else
{
return 0.0; //distanza non valida!
}
}
}
Il metodo dammiDistanza(Punto p) dovrebbe ora funzionare
correttamente.
N.B.: è assolutamente consigliabile apportare una modifica
stilistica al nostro codice, al fine di garantire una migliore
astrazione funzionale:
class Punto3D extends Punto
{
private int z;
. . . //inutile riscrivere l’intera classe
public double dammiDistanza(Punto p)
{
if (p instanceof Punto3D)
{
//Chiamata ad un metodo privato tramite casting
return this.calcolaDistanza((Punto3D)p);
}
else
{
return 0.0; //distanza non valida!
}
}
private double calcolaDistanza(Punto3D p)
{
Punto3D p1=(Punto3D)p; //Casting
int tmp1=(getX()-p1.getX())*(getX()-p1.getX());
//quadrato della differenza della x dei due punti
int tmp2=(getY()-p1.getY())*(getY()-p1. getY());
//quadrato della differenza della y dei due punti
int tmp3=(z-p1.z)*(z-p1.z);
//quadrato della differenza della z dei due punti
return Math.sqrt(tmp1+tpm2+tpm3);
//radice quadrata della somma dei tre quadrati
}
}
N.B.: Java ha tra le sue caratteristiche anche
una potente gestione delle eccezioni, che costituisce uno dei punti
di forza del linguaggio.
- Passo 4:
Abbiamo adesso la possibilità di iniziare a scrivere la "classe del
main". Il nome che dovremmo assegnare ad essa, dovrebbe coincidere
con il nome dell’applicazione stessa. Optiamo per l’identificatore
TestGeometrico, giacché, piuttosto che considerare questa
un’applicazione "finale", preferiamo pensare ad essa come un test,
per un nucleo di classi funzionanti (Punto e Punto3D), che potranno
essere risfruttate in un’applicazione "vera".
class TestGeometrico
{
public static void main(String args[])
{
. . .
}
}
Di solito, quando si inizia ad imparare un nuovo
linguaggio di programmazione, uno dei primi argomenti che
l’aspirante sviluppatore impara a gestire, è l’input – output nelle
applicazioni. Quando invece s’approccia a Java, rimane misterioso
per un certo periodo il "comando" di output:
System.out.println("Stringa da stampare");
ed assolutamente sconosciuto per un lungo periodo una qualsiasi
istruzione che permetta di acquisire dati in input! Ciò è dovuto ad
una ragione ben precisa: le classi che permettono di realizzare
operazioni di input – output, fanno parte del package java.io, della
libreria standard. Questo package è stato progettato con una
filosofia ben precisa, dovuta tra l’altro, all’implementazione del
pattern "Decorator" (per informazioni sui pattern, visitare il sito
http://hillside.net/patterns, per informazioni sul pattern Decorator,
http://www.ugolandini.net ). Il risultato è un’iniziale difficoltà
d’approccio all’argomento, compensata però, da una semplicità ed
efficacia "finale". Per esempio, per un aspirante programmatore può
risultare difficoltoso comprendere le ragioni per cui, per stampare
una stringa a video, i creatori di Java hanno implementato un
meccanismo tanto complesso (System.out.println). Per un
programmatore Java invece, è molto semplice utilizzare gli stessi
metodi per eseguire operazioni di output complesse, come scrivere in
un file, o mandare messaggi via socket. Abbiamo già accennato che in
questa sede non affronteremo argomenti riguardanti la libreria
standard, e quindi ci limiteremo a quanto detto, ed a rimandare il
lettore a testi che trattino l’argomento. Nonostante ciò,
presentiamo di seguito un procedimento che permette di dotare di un
minimo d’interattività le nostre prime applicazioni. Quando
codifichiamo il metodo main, il programmatore è obbligato a fornire
una segnatura che definisca come parametro in input un array di
stringhe (di solito chiamato args). Questo array immagazzinerà
stringhe da riga di comando nel modo seguente. Se per lanciare la
nostra applicazione invece di scrivere a riga di comando:
java nomeClasseDelMain scrivessimo:
java nomeClasseDelMain Claudio De Sio Cesari
all’interno della nostra applicazione, avremmo a disposizione la
stringa args[0] che ha come valore Claudio, la stringa args[1] che
ha come valore De, la stringa args[2] che ha come valore Sio, e la
stringa args[3] che ha come valore Cesari. Potremmo anche scrivere:
java nomeClasseDelMain Claudio "De Sio Cesari"
in questo modo, all’interno dell’applicazione potremmo utilizzare
solamente la stringa args[0] che ha come valore Claudio, e la
stringa args[1] che ha come valore De Sio Cesari.
Codifichiamo finalmente al nostra classe TestGeometrico, sfruttando
quanto detto, ed un metodo per trasformare una stringa in intero:
class TestGeometrico
{
public static void main(String args[])
{
//Conversione a tipo int di stringhe
int p1X=Integer.parseInt(args[0]);
int p1Y=Integer.parseInt(args[1]);
int p2X=Integer.parseInt(args[2]);
int p2Y=Integer.parseInt(args[3]);
//Istanza dei due punti
Punto p1 = new Punto(p1X, p1Y);
Punto p2 = new Punto(p2X, p2Y);
//Stampa della distanza
System.out.println("i punti distano " +
p1.dammiDistanza(p2));
}
}
Possiamo ora lanciare l’applicazione (ovviamente
dopo la compilazione), scrivendo a riga di comando per esempio:
java TestGeometrico 5 6 10 20
l’output del nostro programma sarà:
C:\ >javaTestGeometrico 5 6 10 20
i punti distano 14.866068747318506
N.B.: Per mandare in esecuzione quest’applicazione siamo obbligati a
passare da riga di comando quattro parametri interi, per non
ottenere un’eccezione. In ambito esecutivo infatti, la Java Virtual
Machine, incontrerà variabili con valori indefiniti come args[0].
- Passo 5:
Miglioriamo la nostra applicazione in modo tale che possa calcolare
le distanze anche tra due punti tridimensionali. Introduciamo prima
un test per testare se è stato inserito il giusto numero di
parametri in input: se i parametri sono 4, è eseguita la distanza
tra due punti bidimensionali, se i parametri sono 6, è eseguita la
distanza tra due punti tridimensionali. In ogni altro caso, viene
lanciato un messaggio esplicativo, e viene terminata l’esecuzione
del programma, prevenendone eventuali eccezioni in fase di runtime.
class TestGeometrico
{
public static void main(String args[])
{
//Per ragioni di scope dichiariamo ed inizializziamo
//le variabili locali
Punto p1=null, p2=null;
//testiamo se sono stati inseriti il giusto numero
//di parametri
if (args.length==4)
{
//Conversione a tipo int di stringhe
int p1X=Integer.parseInt(args[0]);
int p1Y=Integer.parseInt(args[1]);
int p2X=Integer.parseInt(args[2]);
int p2Y=Integer.parseInt(args[3]);
//Istanza dei due punti
p1 = new Punto(p1X, p1Y);
p2 = new Punto(p2X, p2Y);
}
else if (args.length==6)
{
//Conversione a tipo int di stringhe
int p1X=Integer.parseInt(args[0]);
int p1Y=Integer.parseInt(args[1]);
int p1Z=Integer.parseInt(args[3]);
int p2X=Integer.parseInt(args[4]);
int p2Y=Integer.parseInt(args[5]);
int p2Z=Integer.parseInt(args[6]);
//Istanza dei due punti
p1 = new Punto(p1X, p1Y, p1Z);
p2 = new Punto(p2X, p2Y, p2Z);
}
else
{
System.out.println(
"inserisci 4 o 6 parametri da
riga di comando");
System.exit(0);
}
//Stampa della distanza
System.out.println("i punti distano " +
p1.dammiDistanza(p2));
}
}
N.B.: le classi Punto e Punto3D, sono assolutamente riutilizzabili
"dietro" altre applicazioni.
N.B.: nei prossimi moduli, in base agli argomenti proposti,
torneremo su questo esercizio facendo osservazioni ed apportando
modifiche.
Esercizio 5.a)
Realizzare un’applicazione che simuli il funzionamento di una
rubrica! Il lettore si limiti a simulare la seguente situazione:
una rubrica contiene informazioni (nome, indirizzo, numero
telefonico) su di un certo numero di persone (per esempio 5)
prestabilito (le informazioni sono pre-introdotte nel metodo main).
L’utente, dovrà fornire all’applicazione un nome da riga di comando,
e l’applicazione dovrà restituire le informazioni relative al nome
della persona. Se il nome non è fornito, o se il nome immesso non
corrisponde al nome di una persona pre-introdotta dall’applicazione,
deve essere restituito un messaggio significativo.
Il lettore non ha altri vincoli.
N.B.: Se il lettore ottiene un risultato implementativo funzionante
e coerente con tutto ciò che ha studiato sino ad ora, può
considerarsi veramente soddisfatto. Infatti, l’esercizio proposto, è
presentato spesso a corsi di formazione da noi erogati, nell
giornata iniziale, per testare il livello della classe. Molto
spesso, anche corsisti che si dichiarano "programmatori Java" con
esperienza e/o conoscenze, non riescono ad ottenere un risultato
accettabile. Ricordiamo una volta di più, che questo testo vuole
avere lo scopo di rendere il lettore capace di programmare in Java
in modo corretto, e senza limiti. Non bisogna avere fretta! Con un
po’ di pazienza iniziale in più, si otterranno risultati
sbalorditivi. Una volta padroni del linguaggio, non esisteranno più
ambiguità e misteri, e l’acquisizione di argomenti che oggi sembrano
avanzati (Applet, Jsp, Servlet, etc…), risulterà semplice!
Elenco delle pagine di "Il linguaggio Java: lez.8"
Unità didattica 7.1, 7.2 e 7.3.a ) -
Pagina 1
Unità didattica 7.3.b) - Pagina 2