Esercizi
- Home
- Esercizi Riepilogativi Parte II
Parte II
Un esempio guidato alla programmazione ad oggetti
Basandoci sull'approfondimento 6.1 intitolato "Un esempio guidato alla programmazione ad oggetti",
abbiamo creato esercizi di riepilogo su tutti gli argomenti che abbiamo studiato sino ad ora. Tutti
gli esercizi prevedono programmazione. In particolare, nella prima parte creeremo un'applicazione
partendo dalla classe Punto
che abbiamo definito in questo capitolo, fino a disegnare
graficamente linee spezzate (polilinee) su un piano cartesiano. Poi creeremo un'applicazione che
capace di convertire i valori espressi in scala Kelvin, Celsius e Fahrenheit. Infine creeremo
un'applicazione che simula un gioco testuale configurabile e multi-utente. Dal prossimo capitolo in
poi, studieremo argomenti più complessi iniziando la terza parte del "Caratteristiche avanzate del
linguaggio", quindi cerchiamo di rinforzare le nostre basi di programmazione.
Di seguito trovate gli esercizi del capitolo.
Per ogni esercizio, cliccando sulla traccia potete vedere la relativa soluzione.
Gli esercizi caratterizzati dall'icona sono
considerati i più complessi relativamente agli argomenti trattati.
Se preferite lavorare offline, è possibile scaricare tutti gli esercizi e le relative
soluzioni in formato PDF nella sezione download.
-
Esercizio 8.aa)
Creare:
• La classePunto
con coordinatex
ey
, i relativi metodi setter e getter, un costruttore e un metodotoString
(che ricordiamo essere un metodo ereditato dalla classeObject
, e quindi ereditato da tutte le classi).
• La classeRighello
che dichiara un metodocalcolaDistanza
che prende in input due oggetti di tipoPunto
e restituisce il valore della distanza geometrica di tipodouble
. Leggere la documentazione e sfruttare i metodi della classeMath
per eseguire i calcoli. Inoltre valutare se dichiarare il metodo statico oppure no.
• Creare una classeEsercizio8AA
, che stampa la distanza di due punti e controllare se il risultato è corretto utilizzando un'asserzione come visto nel capitolo 8.
Per poter utilizzare le asserzioni bisogna abilitarle nel momento dell'esecuzione utilizzando l'opzione-ea
(vedi capitolo 8). Se utilizzate EJE le asserzioni dovrebbero già essere abilitate di default. È possibile controllare all'interno delle opzioni di EJE come mostrato in figura 8.aa.1. Se invece utilizzate un altro IDE, consultare la documentazione dell'IDE.
Figura 8.aa.1 – Abilitazione delle asserzioni in EJE.
Soluzione
Potremmo implementare la classe
Punto
nel seguente modo:
public class Punto { private int x; private int y; public Punto (int x, int y) { setX(x); setY(y); } private void setY(int y) { this.y = y; } public int getY() { return y; } private void setX(int x) { this.x = x; } public int getX() { return x; } public String toString() { return "("+x+"," + y + ")"; } }
Segue la classeRighello
(notare che abbiamo scelto di dichiarare il metodo statico, visto che dipende solo dall'input e non da eventuali variabili d'istanza):
import static java.lang.Math.*; public class Righello { public static double calcolaDistanza(Punto p1, Punto p2) { return sqrt(pow(p1.getX()-p2.getX(), 2) + pow(p1.getY()-p2.getY(), 2)); } }
Infine segue la classeEsercizio8AA
che istanzia due punti, ne calcola la distanza e utilizza un'asserzione:
public class Esercizio8AA { private final static double RISULTATO_CORRETTO = 2; public static void main(String args[]) { Punto p1 = new Punto(0,0); Punto p2 = new Punto(2,0); double distanza = Righello.calcolaDistanza(p1,p2); System.out.println("Distanza tra i punti: " + p1 + " e " + p2 + " = " + distanza); assert distanza == RISULTATO_CORRETTO : "Errore! Il risultato dovrebbe essere " + RISULTATO_CORRETTO; System.out.println("Distanza corretta!"); } }
-
Esercizio 8.bb)
Partendo dall'esercizio precedente, creare la classe
Esercizio8BB
. Questa volta parametrizziamo l'applicazione utilizzando l'array di stringheargs
, parametro di input del metodomain
. In particolare, quando lanciamo la classeEsercizio8BB
, bisogna passare in input le quattro coordinate dei due punti. Per esempio così:
java Esercizio8BB 2 1 1 2
Siccome queste variabili all'interno del metodo main saranno di tipo
String
, usando la documentazione della classeInteger
, trovare il metodo corretto che ci permetterà di tramutare una stringa in un intero, e gestire eventuali eccezioni.
Non è richiesto il controllo fatto con le asserzioni nell'esercizio precedente perché non possiamo sapere a priori i valori delle coordinate che saranno passati al metodomain
.
Soluzione
Il listato della classe
Esercizio8BB
è il seguente:
public class Esercizio8BB { public static void main(String args[]) { if (args.length != 4) { System.out.println("Per calcolare la distanza tra due punti," + " inserire le 4 coordinate"); } try { Punto p1 = new Punto(Integer.parseInt(args[0]), Integer.parseInt(args[1])); Punto p2 = new Punto(Integer.parseInt(args[2]), Integer.parseInt(args[3])); System.out.println("Distanza tra i punti: "+ p1 +" e "+ p2 +" = " + Righello.calcolaDistanza(p1,p2)); } catch (NumberFormatException exc) { System.out.println("Le coordinate devono essere di tipo intero!" + " Errore: "+ exc.getMessage()); } } }
Lanciando l'applicazione con il seguente comando:
java Esercizio8BB 2 1 1 2
otteremo l'output:
Distanza tra i punti: (2,1) e (1,2) = 1.4142135623730951
-
Esercizio 8.cc)
Partendo dalla soluzione dell'esercizio precedente, creare la classe
Esercizio8CC
utilizzando la classeScanner
, per acquisire da riga di comando le coordinate dei due punti. Tale classe deve:
• Chiedere l'inserimento delle coordinate (una per volta).
• Acquisire le coordinate mediante il metodo della classeScanner
più opportuno (consultare la relativa documentazione).
• Se l'utente inserisce una coordinata in un formato non corretto (ricordiamo che le coordinate devono essere numeri interi), il programma deve gestire l'eccezione, stampare un messaggio di errore e terminare.
Segue un esempio di output che dovrebbe generare l'applicazione:
Inserisci coordinata x per il punto 1 2 Inserisci coordinata y per il punto 1 1 Creato punto:(2,1) Inserisci coordinata x per il punto 2 1 Inserisci coordinata y per il punto 2 2 Creato punto:(1,2) Distanza tra i punti: (2,1) e (1,2) = 1.4142135623730951
Soluzione
Segue il codice della classe
Esercizio8CC
:
import java.util.Scanner; import java.util.InputMismatchException; public class Esercizio8CC { public static void main(String args[]) { Scanner scanner = new Scanner(System.in); try { System.out.println("Inserisci coordinata x per il punto 1"); int x1 = scanner.nextInt(); System.out.println("Inserisci coordinata y per il punto 1"); int y1 = scanner.nextInt(); Punto p1 = new Punto(x1, y1); System.out.println("Creato punto:"+ p1); System.out.println("Inserisci coordinata x per il punto 2"); int x2 = scanner.nextInt(); System.out.println("Inserisci coordinata y per il punto 2"); int y2 = scanner.nextInt(); Punto p2 = new Punto(x2, y2); System.out.println("Creato punto:"+ p2); System.out.println("Distanza tra i punti: "+ p1 +" e "+ p2 +" = " + Righello.calcolaDistanza(p1,p2)); } catch (InputMismatchException exc) { System.out.println("Le coordinate devono essere di tipo intero!"); } } }
-
Esercizio 8.dd)
Partendo dalla soluzione dell'esercizio precedente, creare la classe
Esercizio8DD
, eseguendo un refactoring sulla classeEsercizio8CC
, cercando di limitare il codice duplicato e renderlo più leggibile.
Soluzione
Segue il codice della classe
Esercizio8DD
, refactoring della classeEsercizio8CC
:
import java.util.Scanner; import java.util.InputMismatchException; public class Esercizio8DD { private Scanner scanner; public Esercizio8DD () { scanner = new Scanner(System.in); } public void start() { try { Punto p1 = getPunto("1"); Punto p2 = getPunto("2"); stampaDistanza(p1, p2) ; } catch (InputMismatchException exc) { System.out.println("Le coordinate devono essere di tipo intero!"); } } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per il punto "+ nomePunto); return scanner.nextInt(); } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(x,y); System.out.println("Creato punto: "+ p); return p; } public void stampaDistanza(Punto p1, Punto p2) { System.out.println("Distanza tra i punti: "+ p1 +" e "+ p2 +" = " + Righello.calcolaDistanza(p1,p2)); } public static void main(String args[]) { Esercizio8DD esercizio8DD = new Esercizio8DD(); esercizio8DD.start(); } }
-
Esercizio 8.ee)
Partendo dalla soluzione dell'esercizio precedente, creare la classe
Esercizio8EE
, facendo in modo che, se l'utente inserisce una coordinata con un formato sbagliato (per esempio inserendo una lettera), l'applicazione deve segnalare l'errore a permettere all'utente di reinserire la coordinata. Il programma deve terminare solo nel momento in cui verranno inserite tutte le coordinate correttamente e la distanza verrà stampata. Segue un esempio di output che potrebbe generare l'applicazione:
Inserisci coordinata x per il punto 1 f Coordinata non valida f!, Le coordinate devono essere di tipo intero! Prego reinserire 2 Inserisci coordinata y per il punto 1 1 Creato punto:(2,1) Inserisci coordinata x per il punto 2 1 Inserisci coordinata y per il punto 2 y Coordinata non valida y!, Le coordinate devono essere di tipo intero! Prego reinserire pippo Coordinata non valida pippo!, Le coordinate devono essere di tipo intero! Prego reinserire 3 Creato punto:(1,3) Distanza tra i punti: (2,1) e (1,3) = 2.23606797749979
Soluzione
Segue una possibile implementazione della classe
Esercizio8EE
:
import java.util.Scanner; import java.util.InputMismatchException; public class Esercizio8EE { private Scanner scanner; public Esercizio8EE () { scanner = new Scanner(System.in); } public void start() { Punto p1 = getPunto("1"); Punto p2 = getPunto("2"); stampaDistanza(p1, p2) ; } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida " + scanner.next() + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(x,y); System.out.println("Creato punto:"+ p); return p; } public void stampaDistanza(Punto p1, Punto p2) { System.out.println("Distanza tra i punti: "+ p1 +" e "+ p2 +" = " + Righello.calcolaDistanza(p1,p2)); } public static void main(String args[]) { Esercizio8EE esercizio8EE = new Esercizio8EE(); esercizio8EE.start(); } }
-
Esercizio 8.ff)
Partendo dalla soluzione dell'esercizio precedente:
• creare la classeSegmento
, che astrae il concetto di linea retta compresa tra due punti. Tale classe deve dichiarare come variabili d'istanza i due estremi e la distanza. Creare anche i metodi setter e getter che si ritiene di dover implementare, un costruttore e un metodotoString
.
• creare la classeEsercizio8FF
, sulla falsariga della classeEsercizio8EE
, che chiede le coordinate dei due estremi del segmento e ne stampa la lunghezza.
Segue un esempio di output che potrebbe generare l'applicazione:
Definiamo un segmento specificando i due estremi: Inserisci coordinata x per l'estremo 1 2 Inserisci coordinata y per l'estremo 1 pippo Coordinata non valida pippo!, Le coordinate devono essere di tipo intero! Prego reinserire 3 Creato estremo:(2,3) Inserisci coordinata x per l'estremo 2 1 Inserisci coordinata y per l'estremo 2 1 Creato estremo:(1,1) Segmento da P1(2,3) a P2(1,1) con lunghezza = 2.23606797749979
Soluzione
Il listato della nostra classe
Segmento
potrebbe essere il seguente:
public class Segmento { private Punto estremo1; private Punto estremo2; private double lunghezza; public Segmento(Punto estremo1, Punto estremo2 ) { this.estremo1 = estremo1; this.estremo2 = estremo2; setLunghezza(); } public Punto getEstremo1() { return estremo1; } public Punto getEstremo2() { return estremo2; } private void setLunghezza() { this.lunghezza = Righello.calcolaDistanza(estremo1, estremo2); } public String toString() { return "Segmento da P1"+estremo1+" a P2"+estremo2 + " con lunghezza = " + lunghezza; } }
Infine abbiamo implementato la classeEsercizio8FF
come segue:
import java.util.*; public class Esercizio8FF { private Scanner scanner; public Esercizio8FF () { scanner = new Scanner(System.in); } public void start() { System.out.println("Definiamo un segmento specificando i due estremi:"); Punto p1 = getPunto("1"); Punto p2 = getPunto("2"); stampa(new Segmento(p1, p2) ); } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per l'estremo "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida " + scanner.next() + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(x,y); System.out.println("Creato estremo:"+ p); return p; } public void stampa(Segmento segmento) { System.out.println(segmento); } public static void main(String args[]) { Esercizio8FF esercizio8FF = new Esercizio8FF(); esercizio8FF.start(); } }
-
Esercizio 8.gg)
In questo esercizio utilizzeremo una classe chiama
CartesianPlane
(in italiano "piano cartesiano"), che ci permetterà di visualizzare i nostri punti su un piano cartesiano.
Purtroppo, a meno che non abbiate già studiato tutti i capitoli di questo libro, non dovreste essere in grado di capirne tutto il codice. Tuttavia per completezza ne riportiamo il codice di seguito (trovate comunque la classe già pronta nel file degli esercizi nella cartella 8.gg):
import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JFrame; import javax.swing.SwingUtilities; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Dimension; import java.util.*; public class CartesianPlane { private final static int LENGTH = 600; private final static int MID_LENGTH = LENGTH/2; private final static int GAP = LENGTH/10; private JFrame frame; private JPanel panel; private ArrayList<Punto> points; public CartesianPlane() { frame = new JFrame("Cartesian Plane"); panel = new CartesianPlanePanel(); points = new ArrayList<>(); setup(); addDetails(); } private void setup() { frame.add(panel); } private void addDetails() { frame.getContentPane().setPreferredSize(new Dimension(LENGTH , LENGTH)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public void drawPoint(Punto point) { points.add(point); panel.repaint(); } private class CartesianPlanePanel extends JPanel { public CartesianPlanePanel() { setBackground(Color.lightGray); } protected void paintComponent(Graphics g) { super.paintComponent(g); drawAxes(g); g.setColor(Color.RED); drawPoints(g); } private void drawPoints(Graphics g) { for (Punto point : points) { drawPoint(point, g); } } private void drawPoint(Punto point, Graphics g) { int x = getX(point.getX(), 2); int y = getY(point.getY(), 2); g.drawOval(x,y,5,5); g.fillOval(x,y,5,5); g.drawString(point.toString(), x-15, y-8); } private int getX(int x, int delta) { return MID_LENGTH + (gap(x)-delta); } private int getY(int y, int delta) { return MID_LENGTH - (gap(y)+delta); } private void drawAxes(Graphics g) { drawReferencePoints(g); g.setColor(Color.BLACK); g.drawLine(MID_LENGTH, 0, MID_LENGTH, LENGTH); g.drawLine(0, MID_LENGTH, LENGTH, MID_LENGTH); } private void drawReferencePoints(Graphics g) { g.setColor(Color.YELLOW); for(int i = 1; i < 10; i++) { g.drawLine(GAP*i, 0, GAP*i, LENGTH); } for(int i = 1; i < 10; i++) { g.drawLine(0, GAP*i, LENGTH, GAP*i); } } private int gap(int value) { return value*GAP; } } }
È il primo esercizio di programmazione dove vedrete un'applicazione che utilizza la grafica (a questo argomento sono dedicati gli ultimi due capitoli del libro). In qualsiasi caso, lo scopo del nostro esercizio non è quello di comprendere il codice della classe
CartesianPlane
, quanto quello di utilizzare questa classe creando la classeEsercizio8GG
a partire dalla classeEsercizio8FF
. La classeEsercizio8GG
dovrà aprire il pannello del cartello cartesiano con al seguente istruzione:
SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane());
(importando
javax.swing. SwingUtilities
).
Inoltre dovrà:
• Aggiungere alla classePunto
una nuova variabile d'istanzanome
, con relativi metodi setter e getter e un nuovo costruttore.
• Costruire un punto con le coordinate inserite dall'utente. Settare anche il nome del punto, con un meccanismo automatico (per esempio il primo punto deve chiamarsiP1
, il secondoP2
, il terzoP3
, e così via).
• Invece di stampare i dettagli del punto sulla console, la classe deve invocare il metododrawPoint
della classeCartesianPlane
.
Verificare che i punti vengano visualizzati correttamente sul piano cartesiano (vedi figura 8.gg.1).
Figura 8.gg.1 – Esempio di funzionamento dell'applicazione.
Soluzione
Abbiamo modificato la classe
Punto
nel seguente modo:
public class Punto { private String nome; private int x; private int y; public Punto (String nome, int x, int y) { setNome(nome); setX(x); setY(y); } public Punto (int x, int y) { this("", x, y); } public void setNome(String nome) { this.nome = nome; } public String getNome() { return nome; } private void setY(int y) { this.y = y; } public int getY() { return y; } private void setX(int x) { this.x = x; } public int getX() { return x; } public String toString() { return nome + "("+x+","+ y +")"; } }
Infine abbiamo implementato la classeEsercizio8GG
come segue:
import java.util.*; import javax.swing.*; public class Esercizio8GG { private Scanner scanner; private CartesianPlane cartesianPlane; public Esercizio8GG () { scanner = new Scanner(System.in); } public void start() { SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane()); Punto p1 = getPunto("P1"); cartesianPlane.drawPoint(p1); } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata +" per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida " + scanner.next() + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(nomePunto, x,y); System.out.println("Creato punto:"+ p); return p; } public static void main(String args[]) { Esercizio8GG esercizio8GG = new Esercizio8GG(); esercizio8GG.start(); } }
-
Esercizio 8.hh)
Partendo dall'esercizio precedente far evolvere la classe
Esercizio8GG
, nella classeEsercizio8HH
. Questa deve continuare a chiedere all'utente di inserire le coordinate di un punto all'infinito, sino a quando l'applicazione non verrà chiusa chiudendo la finestra del piano cartesiano. In questo modo l'utente potrà visualizzare più punti sull'asse cartesiano.
La risoluzione di questo esercizio è molto semplice.
Soluzione
Il listato della classe
Esercizio8HH
è il seguente:
import java.util.*; import javax.swing.*; public class Esercizio8HH { private Scanner scanner; private CartesianPlane cartesianPlane; private static int contatore = 1; public Esercizio8HH () { scanner = new Scanner(System.in); } public void start() { SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane()); while(true) { Punto p1 = getPunto("P"+(contatore++)); cartesianPlane.drawPoint(p1); } } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida " + scanner.next() + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(nomePunto, x,y); System.out.println("Creato punto:"+ p); return p; } public static void main(String args[]) { Esercizio8HH esercizio8HH = new Esercizio8HH(); esercizio8HH.start(); } }
-
Esercizio 8.ii)
Partendo dall'esercizio precedente, e utilizzando la classe
Segmento
realizzata nell'esercizio 8.ff, forniamo una nuova versione della classeCartesianPlane
, a cui abbiamo aggiunto la funzionalità di poter visualizzare segmenti (basta invocare il metododrawSegment
). La nuova classeCartesianPlane
è riportata di seguito (trovate comunque la classe già pronta nel file degli esercizi nella cartella 8.ii):
import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JFrame; import javax.swing.SwingUtilities; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Dimension; import java.util.*; public class CartesianPlane { private final static int LENGTH = 600; private final static int MID_LENGTH = LENGTH/2; private final static int GAP = LENGTH/10; private JFrame frame; private JPanel panel; private ArrayList<Punto> points; private ArrayList<Segmento> segments; public CartesianPlane() { frame = new JFrame("Cartesian Plane"); panel = new CartesianPlanePanel(); points = new ArrayList<>(); segments = new ArrayList<>(); setup(); addDetails(); } private void setup() { frame.add(panel); } private void addDetails() { frame.getContentPane().setPreferredSize(new Dimension(LENGTH , LENGTH)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public void drawPoint(Punto point) { points.add(point); panel.repaint(); } public void drawSegment(Segmento segment) { segments.add(segment); panel.repaint(); } private class CartesianPlanePanel extends JPanel { public CartesianPlanePanel() { setBackground(Color.lightGray); } protected void paintComponent(Graphics g) { super.paintComponent(g); drawAxes(g); g.setColor(Color.RED); drawPoints(g); g.setColor(Color.BLUE); drawSegments(g); } private void drawSegments(Graphics g) { for (Segmento segment : segments) { Punto estremo1 = segment.getEstremo1(); drawPoint(estremo1, g); Punto estremo2 = segment.getEstremo2(); drawPoint(estremo2, g); g.setColor(Color.BLUE); g.drawLine(getX(estremo1.getX()), getY(estremo1.getY()), getX(estremo2.getX()), getY(estremo2.getY())); } } private void drawPoints(Graphics g) { for (Punto point : points) { drawPoint(point, g); } } private void drawPoint(Punto point, Graphics g) { int x = getX(point.getX(), 2); int y = getY(point.getY(), 2); g.drawOval(x,y,5,5); g.fillOval(x,y,5,5); g.drawString(point.toString(), x-15, y-8); } private int getX(int x) { return getX(x, 0); } private int getY(int y) { return getY(y, 0); } private int getX(int x, int delta) { return MID_LENGTH + (gap(x)-delta); } private int getY(int y, int delta) { return MID_LENGTH - (gap(y)+delta); } private void drawAxes(Graphics g) { drawReferencePoints(g); g.setColor(Color.BLACK); g.drawLine(MID_LENGTH, 0, MID_LENGTH, LENGTH); g.drawLine(0, MID_LENGTH, LENGTH, MID_LENGTH); } private void drawReferencePoints(Graphics g) { g.setColor(Color.YELLOW); for(int i = 1; i < 10; i++) { g.drawLine(GAP*i, 0, GAP*i, LENGTH); } for(int i = 1; i < 10; i++) { g.drawLine(0, GAP*i, LENGTH, GAP*i); } } private int gap(int value) { return value*GAP; } }
Sulla falsariga della classe
Esercizio8HH
, creare una classeEsercizio8II
che permette all'utente specificare gli estremi di segmenti che devono essere visualizzati sul piano cartesiano.
Soluzione
Segue la classe
Esercizio8II
:
import java.util.*; import javax.swing.*; public class Esercizio8II { private Scanner scanner; private CartesianPlane cartesianPlane; private static int contatore = 1; public Esercizio8II () { scanner = new Scanner(System.in); } public void start() { SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane()); while(true) { System.out.println("Definiamo un segmento"); Punto p1 = getPunto("P"+(contatore++)); Punto p2 = getPunto("P"+(contatore++)); Segmento s1 = new Segmento(p1, p2); cartesianPlane.drawSegment(s1); } } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida + scanner.next()" + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(nomePunto, x,y); System.out.println("Creato punto:"+ p); return p; } public static void main(String args[]) { Esercizio8II esercizio8II = new Esercizio8II(); esercizio8II.start(); } }
-
Esercizio 8.jj)
Partendo dalla soluzione dell'esercizio precedente, creare una classe
Polilinea
, che astrae il concetto di polilinea (conosciuta anche come linea spezzata). Questa deve dichiarare unArrayList
di segmenti. Una polilinea ha il vincolo che il secondo estremo di un segmento, deve coincidere con il primo estremo del segmento successivo. Tenere presente che inoltre una polilinea è sempre costituita da almeno due segmenti. Quindi creare uno o più costruttori, il metodoaggiungiSegmento
che permette di aggiungere un segmento alla polilinea, e il metodotoString
. Gestire eventuali problemi con le eccezioni, come studiato nel capitolo 8.
Creare inoltre una classe di test che chiameremoTestPolilinea
che avrà il seguente metodomain
:
public static void main(String args[]) { testPolilineaCorretta(); testPolilineaCorrettaConQuattroPunti(); testPolilineaSenzaSegmenti(); testPolilineaConUnSoloSegmento(); testPolilineaConDueSegmentiNonConsecutiviNelCostruttore(); testPolilineaConTreSegmentiNonConsecutiviNelCostruttore(); testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo(); testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo(); }
Implementare tali metodi con delle asserzioni (se il test fallisce, deve essere lanciato un errore tramite un'asserzione).
Soluzione
Abbiamo implementato la classe
Polilinea
nella seguente maniera:
import java.util.*; public class Polilinea { private List<Segmento> segmentiList; public Polilinea (Segmento... segmenti) throws PolilineaNonValidaException { this.segmentiList = new ArrayList(Arrays.asList(segmenti)); if (segmenti.length < 2 || !controllaSegmentiConsecutivi()) { throw new PolilineaNonValidaException(segmentiList); } } public void aggiungiSegmento(Segmento nuovoSegmento) throws PolilineaNonValidaException { Segmento ultimoSegmento = segmentiList.get(segmentiList.size()-1); Punto estremo2UltimoSegmento = ultimoSegmento.getEstremo2(); Punto estremo1NuovoSegmento = nuovoSegmento.getEstremo1(); segmentiList.add(nuovoSegmento); if (!uguali(estremo1NuovoSegmento, estremo2UltimoSegmento)) { throw new PolilineaNonValidaException(segmentiList); } } private boolean controllaSegmentiConsecutivi() { int segmentiListSize = segmentiList.size(); for (int i = 0; i < segmentiListSize-1; ) { Punto secondoEstremoSegmentoPrecedente = segmentiList.get(i++).getEstremo2(); Punto primoEstremoSegmentoSuccessivo = segmentiList.get(i).getEstremo1(); if (!uguali(secondoEstremoSegmentoPrecedente, primoEstremoSegmentoSuccessivo)) { return false; } } return true; } private boolean uguali(Punto p1, Punto p2) { return p1.getX()==p2.getX() && p1.getY()== p2.getY(); } public String toString() { String descrizione ="Polilinea formata da:\n"; for (Segmento segmento : segmentiList) { descrizione += segmento +"\n"; } return descrizione; } }
Dove la classePolilineaNonValidaException
è implementata come segue:
import java.util.List; public class PolilineaNonValidaException extends Exception { public PolilineaNonValidaException (List<Segmento> segmenti) { super(segmenti.size() < 2 ? "Una polilinea deve essere costituita da" + " almeno 2 segmenti" : "Questi segmenti " + segmenti + " non costituiscono una polilinea"); } public String toString() { return "Polilinea non valida:\n" + getMessage(); } }
Infine l'implementazione della classeTestPolilinea
non è semplice:
public class TestPolilinea { private static final String TEST_OK ="TEST OK:\n"; private static final String TEST_KO ="TEST FALLITO: "; public static void main(String args[]) { testPolilineaCorretta(); testPolilineaCorrettaConQuattroPunti(); testPolilineaSenzaSegmenti(); testPolilineaConUnSoloSegmento(); testPolilineaConDueSegmentiNonConsecutiviNelCostruttore(); testPolilineaConTreSegmentiNonConsecutiviNelCostruttore(); testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo(); testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo(); } public static void testPolilineaCorretta() { try { System.out.println("testPolilineaCorretta:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); System.out.println(TEST_OK + pol1); } catch (Exception exc) { assert false : TEST_KO + exc.getMessage(); } } public static void testPolilineaCorrettaConQuattroPunti() { try { System.out.println("testPolilineaCorrettaConQuattroPunti:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,1); Punto p4 = new Punto(2,3); Segmento s2 = new Segmento(p3, p4); Polilinea pol1 = new Polilinea(s1, s2); System.out.println(TEST_OK + pol1); } catch (Exception exc) { assert false : TEST_KO + exc.getMessage(); } } public static void testPolilineaSenzaSegmenti() { try { System.out.println("testPolilineaSenzaSegmenti:"); Polilinea pol1 = new Polilinea(); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea senza segmenti!"; } catch (Exception exc) { System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public static void testPolilineaConUnSoloSegmento() { try { System.out.println("testPolilineaConUnSoloSegmento:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Polilinea pol1 = new Polilinea(s1); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea con un solo segmento!"; } catch (Exception exc) { System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public static void testPolilineaConDueSegmentiNonConsecutiviNelCostruttore() { try { System.out.println( "testPolilineaConDueSegmentiNonConsecutiviNelCostruttore:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,2); Punto p4 = new Punto(2,2); Segmento s2 = new Segmento(p3, p4); Polilinea pol1 = new Polilinea(s1, s2); System.out.println(TEST_KO+ pol1); assert false : "È stata creata un polilinea con due segmenti " + "non consecutivi (nel costruttore)!"; } catch (Exception exc) { System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public static void testPolilineaConTreSegmentiNonConsecutiviNelCostruttore() { try { System.out.println( "testPolilineaConTreSegmentiNonConsecutiviNelCostruttore:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,1); Punto p4 = new Punto(2,2); Segmento s2 = new Segmento(p3, p4); Punto p5 = new Punto(2,3); Punto p6 = new Punto(3,2); Segmento s3 = new Segmento(p5, p6); Polilinea pol1 = new Polilinea(s1, s2, s3); System.out.println(TEST_KO+ pol1); assert false : "È stata creata un polilinea con tre segmenti " + "non consecutivi (nel costruttore)!"; } catch (Exception exc) { System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public static void testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo() { try { System.out.println( "testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); Punto p4 = new Punto(3,2); Segmento s3 = new Segmento(p3, p4); pol1.aggiungiSegmento(s3); System.out.println(TEST_OK+ pol1); } catch (Exception exc) { assert false : TEST_KO + exc.getMessage(); } } public static void testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo() { try { System.out.println( "testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo:"); Punto p1 = new Punto(0,0); Punto p2 = new Punto(1,1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1,2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); Punto p4 = new Punto(3,2); Punto p5 = new Punto(3,3); Segmento s3 = new Segmento(p4, p5); pol1.aggiungiSegmento(s3); System.out.println(TEST_KO+ pol1); assert false :"È stata creata un polilinea con un segment" + " non consecutivo!"; } catch (Exception exc) { System.out.println(TEST_OK + exc.getMessage()); } } }
-
Esercizio 8.kk)
Nell'approfondimento 8.2, abbiamo eseguito questo test con JUnit:
import org.junit.Assert; import org.junit.Test; public class TestPunto{ @Test public void testDammiDistanzaSullAscissa() { Punto p1 = new Punto(1,1); Punto p2 = new Punto(1,2); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == 1); } @Test public void testDammiDistanzaConNull() { Punto p1 = new Punto(1,1); Punto p2 = null; double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == -1); } @Test public void testDammiDistanzaDalloStessoPunto() { Punto p1 = new Punto(1,1); Punto p2 = new Punto(1,1); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == 0); } @Test public void testDammiDistanzaDaUnPunto3D() { Punto p1 = new Punto(1,1); Punto p2 = new Punto3D(1,2,2); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == -1); } }
sulle classi
Punto
:
public class Punto { private int x, y; public Punto() { //Costruttore senza parametri } public Punto(int x, int y) { this.setXY(x, y); //Il this è facoltativo //riutilizziamo codice } public void setX(int x) { this.x = x; //Il this non è facoltativo } public void setY(int y) { this.y = y; //Il this non è facoltativo } public void setXY(int x, int y) { this.setX(x); //Il this è facoltativo this.setY(y); } public int getX() { return this.x; //Il this è facoltativo } public int getY() { return this.y; //Il this è facoltativo } public double dammiDistanza(Punto p) { //quadrato della differenza delle x dei due punti int tmp1 = (x - p.x)*(x - p.x); //quadrato della differenza della y dei due punti int tmp2 = (y - p.y)*(y - p.y); //radice quadrata della somma dei due quadrati return Math.sqrt(tmp1 + tmp2); } }
e
Punto3D
:
public class Punto3D extends Punto { private int z; public Punto3D() { //Costruttore senza parametri } public Punto3D(int x, int y, int z) { this.setXYZ(x, y, z); //Riuso di codice } public void setZ(int z) { this.z = z; //Il this non è facoltativo } public void setXYZ(int x, int y, int z) { this.setXY(x, y); //Riuso del codice this.setZ(z); //Il this è facoltativo } public int getZ() { return this.z; //Il this è facoltativo } @Override public double dammiDistanza(Punto p) { if (p instanceof Punto3D) { //Chiamata ad un metodo privato tramite casting return this.calcolaDistanza((Punto3D)p); } else { return -1; //distanza non valida! } } private double calcolaDistanza(Punto3D p1) { //quadrato della differenza della x dei due punti int tmp1=(getX()-p1.getX())*(getX()-p1.getX()); //quadrato della differenza della y dei due punti int tmp2=(getY()-p1.getY())*(getY()-p1.getY()); //quadrato della differenza della z dei due punti int tmp3=(z-p1.z)*(z-p1.z); //radice quadrata della somma dei tre quadrati return Math.sqrt(tmp1+tmp2+tmp3); } }
ottenendo il fallimento nel 50% dei test. In questo esercizio modificheremo la classe
TestPunto
, risolvendo i problemi evidenziati dal test.
Per semplicità si consiglia di utilizzare un IDE come Eclipse che già integra lo strumento JUnit.
Soluzione
In realtà gli errori evidenziati dal nostro test, erano più che altro dovuti ad errori del test stesso. Questo sta a sottolineare che possiamo creare bachi anche nei test. Quindi bisogna fare molta attenzione. Infatti nel
testDammiDistanzaConNull
è corretto che venga lanciata unaNullPointerException
(anche se sarebbe il caso creare un'eccezione personalizzata per gestire questo problema), mentre nel metodotestDammiDistanzaDaUnPunto3Davevamo
previsto che il risultato della distanza sarebbe stato-1
, ma ovviamente il valore di una distanza non può coincidere con un numero negativo. In questo caso abbiamo risolto cambiando il segno, visto che in fondo avevamo deciso di permettere questo tipo di operazione (avremmo anche potuto implementare una soluzione che lanciasse un'altra eccezione personalizzata visto che avremmo potuto supporre che non è possibile eseguire il calcolo della distanza tra un punto che si trova su un riferimento a due coordinate, da un punto che si trova su un riferimento a tre coordinate). In qualsiasi caso riportiamo di seguito la nuova classeTestPunto
.
import org.junit.Assert; import org.junit.Test; public class TestPunto{ @Test public void testDammiDistanzaSullAscissa() { Punto p1 = new Punto(1,1); Punto p2 = new Punto(1,2); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == 1); } @Test (expected = NullPointerException.class) public void testDammiDistanzaConNull() { Punto p1 = new Punto(1,1); Punto p2 = null; double distanza = p1.dammiDistanza(p2); //Assert.assertTrue(distanza == -1); } @Test public void testDammiDistanzaDalloStessoPunto() { Punto p1 = new Punto(1,1); Punto p2 = new Punto(1,1); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == 0); } @Test public void testDammiDistanzaDaUnPunto3D() { Punto p1 = new Punto(1,1); Punto p2 = new Punto3D(1,2,2); double distanza = p1.dammiDistanza(p2); Assert.assertTrue(distanza == 1); } }
-
Esercizio 8.ll)
Partendo dalla soluzione dell'esercizio 8.jj, trasformare la classe
TestPolilinea
, utilizzando invece delle asserzioni il tool JUnit.
Per semplicità si consiglia di utilizzare un IDE come Eclipse che già integra lo strumento JUnit.
Soluzione
Segue l'implementazione della nuova classe
TestPolilinea
(tra le soluzioni troverete l'intero progetto da importare in Eclipse):
import org.junit.Assert; import org.junit.Test; import static org.junit.jupiter.api.Assertions.assertThrows; public class TestPolilinea { private static final String TEST_OK ="TEST OK:\n"; private static final String TEST_KO ="TEST FALLITO: "; // public static void main(String args[]) { // testPolilineaCorretta(); // testPolilineaCorrettaConQuattroPunti(); // testPolilineaSenzaSegmenti(); // testPolilineaConUnSoloSegmento(); // testPolilineaConDueSegmentiNonConsecutiviNelCostruttore(); // testPolilineaConTreSegmentiNonConsecutiviNelCostruttore(); // testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo(); // testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo(); // } @Test public void testPolilineaCorretta() { try { System.out.println("testPolilineaCorretta:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); Assert.assertNotNull(pol1); } catch (Exception exc) { exc.printStackTrace(); } } @Test public void testPolilineaCorrettaConQuattroPunti() { try { System.out.println("testPolilineaCorrettaConQuattroPunti:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 1); Punto p4 = new Punto(2, 3); Segmento s2 = new Segmento(p3, p4); Polilinea pol1 = new Polilinea(s1, s2); Assert.assertNotNull(pol1); } catch (Exception exc) { exc.printStackTrace(); } } @Test public void testPolilineaSenzaSegmenti() { try { System.out.println("testPolilineaSenzaSegmenti:"); Polilinea pol1 = new Polilinea(); assert false :"È stata creata un polilinea senza segmenti!"; } catch (Exception exc) { Assert.assertNotNull(exc); System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } @Test public void testPolilineaConUnSoloSegmento() { try { System.out.println("testPolilineaConUnSoloSegmento:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Polilinea pol1 = new Polilinea(s1); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea con un solo segmento!"; } catch (Exception exc) { Assert.assertNotNull(exc); System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public void testPolilineaConDueSegmentiNonConsecutiviNelCostruttore() { try { System.out.println( "testPolilineaConDueSegmentiNonConsecutiviNelCostruttore:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 2); Punto p4 = new Punto(2, 2); Segmento s2 = new Segmento(p3, p4); Polilinea pol1 = new Polilinea(s1, s2); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea con due segmenti non " + "consecutivi (nel costruttore)!"; } catch (Exception exc) { Assert.assertNotNull(exc); System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } public void testPolilineaConTreSegmentiNonConsecutiviNelCostruttore() { try { System.out.println( "testPolilineaConTreSegmentiNonConsecutiviNelCostruttore:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 1); Punto p4 = new Punto(2, 2); Segmento s2 = new Segmento(p3, p4); Punto p5 = new Punto(2, 3); Punto p6 = new Punto(3, 2); Segmento s3 = new Segmento(p5, p6); Polilinea pol1 = new Polilinea(s1, s2, s3); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea con tre segmenti non " + "consecutivi (nel costruttore)!"; } catch (Exception exc) { Assert.assertNotNull(exc); System.out.println(TEST_OK + exc.getMessage()); System.out.println(); } } @Test public void testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo() { try { System.out.println( "testPolilineaCorrettaAggiungendoUnSegmentoConsecutivo:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); Punto p4 = new Punto(3, 2); Segmento s3 = new Segmento(p3, p4); pol1.aggiungiSegmento(s3); System.out.println(TEST_OK + pol1); } catch (Exception exc) { exc.printStackTrace(); } } @Test public void testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo() { try { System.out.println( "testPolilineaScorrettaAggiungendoUnSegmentoNonConsecutivo:"); Punto p1 = new Punto(0, 0); Punto p2 = new Punto(1, 1); Segmento s1 = new Segmento(p1, p2); Punto p3 = new Punto(1, 2); Segmento s2 = new Segmento(p2, p3); Polilinea pol1 = new Polilinea(s1, s2); Punto p4 = new Punto(3, 2); Punto p5 = new Punto(3, 3); Segmento s3 = new Segmento(p4, p5); pol1.aggiungiSegmento(s3); System.out.println(TEST_KO + pol1); assert false :"È stata creata un polilinea con un segmento non " + "consecutivo!"; } catch (Exception exc) { Assert.assertNotNull(exc); System.out.println(TEST_OK + exc.getMessage()); } } }
-
Esercizio 8.mm)
Partendo dalla soluzione dell'esercizio 8.ii, ed aggiungendo le classi
Polilinea
ePolilineaNonValidaException
definite nell'esercizio 8.jj, creare sulla falsariga della classeEsercizio8II
, la classeEsercizio8MM
che consente all'utente di creare polilinee sul piano cartesiano. L'applicazione dovrà richiedere all'utente di inserire punto dopo punto gli estremi dei segmenti che costituiscono una polilinea (che nel frattempo verrà visualizzata dalla classeCartesianPlane
). Solo per il primo segmento verrà specificato il primo estremo, mentre per i successivi, è scontato che il primo estremo coincide con il secondo estremo del segmento precedente.
Segue un esempio dell'output che dovrà generare la nostra applicazione:
Definiamo i primi due segmenti di una polilinea : Definiamo il primo estremo del primo segmento della polilinea: Inserisci coordinata x per il punto P1 1 Inserisci coordinata y per il punto P1 2 Creato punto:P1(1,2) Definiamo il secondo estremo del primo segmento della polilinea: Inserisci coordinata x per il punto P2 3 Inserisci coordinata y per il punto P2 4 Creato punto:P2(3,4) Definiamo il secondo estremo del secondo segmento della polilinea: Inserisci coordinata x per il punto P3 5 Inserisci coordinata y per il punto P3 6 Creato punto:P3(5,6) Inserisci coordinata x per il punto P4 3 Inserisci coordinata y per il punto P4 3 Creato punto:P4(3,3) Inserisci coordinata x per il punto P5
Soluzione
Segue l'implementazione della classe
Esercizio8MM
ottenuta modificando la classeEsercizio8II
:
import java.util.*; import javax.swing.*; public class Esercizio8MM { private Scanner scanner; private CartesianPlane cartesianPlane; private static int contatore = 1; public Esercizio8MM () { scanner = new Scanner(System.in); } public void start() { SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane()); System.out.println("Definiamo i primi due segmenti di una polilinea :"); System.out.println("Definiamo il primo estremo del primo segmento della" + " polilinea:"); Punto p1 = getPunto("P"+(contatore++)); System.out.println("Definiamo il secondo estremo del primo segmento " + "della polilinea:"); Punto p2 = getPunto("P"+(contatore++)); Segmento s1 = new Segmento(p1, p2); cartesianPlane.drawSegment(s1); System.out.println("Definiamo il secondo estremo del secondo" + " segmento della" + " polilinea:"); Punto p3 = getPunto("P"+(contatore++)); Segmento s2 = new Segmento(p2, p3); cartesianPlane.drawSegment(s2); Punto ultimoEstremo = p3; while(true) { Punto prossimoEstremo = getPunto("P"+(contatore++)); Segmento prossimoSegmento = new Segmento(ultimoEstremo, prossimoEstremo); cartesianPlane.drawSegment(prossimoSegmento); ultimoEstremo = prossimoEstremo; } } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata +" per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida "+ scanner.next() + "!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(nomePunto, x,y); System.out.println("Creato punto:"+ p); return p; } public static void main(String args[]) { Esercizio8MM esercizio8MM = new Esercizio8MM(); esercizio8MM.start(); } }
-
Esercizio 8.nn)
Testando la soluzione dell'esercizio 8.mm, abbiamo notato che specificando un segmento di estremi coincidenti, il sistema disegna due punti uno sopra l'altro, quindi non si dovrebbe neanche parlare di segmento. Gestire quindi l'eccezione affinché il sistema non permetta di creare segmenti con due estremi coincidenti.
Soluzione
Creiamo dapprima una semplice eccezione per la creazione errata di un segmento in questo modo:
public class SegmentoNonValidoException extends Exception { public String toString() { return "Gli estremi di un segmento non possono coincidere!"; } }
Poi lanciamo questa eccezione nella classeSegmento
nel caso ci sia coincidenza dei due estremi:
public class Segmento { private Punto estremo1; private Punto estremo2; private double lunghezza; public Segmento(Punto estremo1, Punto estremo2 ) throws SegmentoNonValidoException { this.estremo1 = estremo1; this.estremo2 = estremo2; setLunghezza(); } public Punto getEstremo1() { return estremo1; } public Punto getEstremo2() { return estremo2; } private void setLunghezza() throws SegmentoNonValidoException { this.lunghezza = Righello.calcolaDistanza(estremo1, estremo2); if (lunghezza == 0) { throw new SegmentoNonValidoException(); } } public String toString() { return "Segmento da "+estremo1+" a "+estremo2 + " con lunghezza = " + lunghezza; } }
Infine facciamo evolvere la nostra classeEsercizio8MM
nella classeEsercizio8NN
, andando a creare un metodo ricorsivocreaSegmento
, che gestisce l'eccezione richiamando sé stesso. Notiamo anche che abbiamo fatto diventare la variabile temporaneaultimoEstremo
, una variabile d'istanza:
import java.util.*; import javax.swing.*; public class Esercizio8NN { private Scanner scanner; private CartesianPlane cartesianPlane; private Punto ultimoEstremo; private static int contatore = 1; public Esercizio8NN () { scanner = new Scanner(System.in); } public void start() { SwingUtilities.invokeLater(() -> cartesianPlane = new CartesianPlane()); System.out.println("Definiamo i primi due segmenti di una polilinea:"); System.out.println( "Definiamo il primo estremo del primo segmento della polilinea:"); Punto p1 = getPunto("P"+(contatore++)); System.out.println( "Definiamo il secondo estremo del primo segmento della polilinea:"); creaSegmento(p1); /* Punto p2 = getPunto("P"+(contatore++)); Segmento s1 = new Segmento(p1, p2); cartesianPlane.drawSegment(s1);*/ System.out.println( "Definiamo il secondo estremo del secondo segmento della" + " polilinea:"); creaSegmento(ultimoEstremo); /* Punto p3 = getPunto("P"+(contatore++)); Segmento s2 = new Segmento(p2, p3); cartesianPlane.drawSegment(s2); Punto ultimoEstremo = p3;*/ while(true) { /* Punto prossimoEstremo = getPunto("P"+(contatore++)); Segmento prossimoSegmento = new Segmento(ultimoEstremo, prossimoEstremo); cartesianPlane.drawSegment(prossimoSegmento); ultimoEstremo = prossimoEstremo; */ creaSegmento(ultimoEstremo); } } public void creaSegmento(Punto primoEstremo) { try { ultimoEstremo = getPunto("P"+(contatore++)); Segmento segmento = new Segmento(primoEstremo, ultimoEstremo); cartesianPlane.drawSegment(segmento); } catch (SegmentoNonValidoException exc) { System.out.println(exc); contatore--; creaSegmento(primoEstremo); } } private int getCoordinata(String nomeCoordinata, String nomePunto) { System.out.println("Inserisci coordinata "+ nomeCoordinata + " per il punto "+ nomePunto); while (scanner.hasNext()) { if (scanner.hasNextInt()) { return scanner.nextInt(); } else { System.out.println("Coordinata non valida "+ scanner.next() +"!, Le coordinate devono essere di tipo intero! " + "Prego reinserire"); } } return -1; } private Punto getPunto(String nomePunto) { int x = getCoordinata("x", nomePunto); int y = getCoordinata("y", nomePunto); Punto p = new Punto(nomePunto, x,y); System.out.println("Creato punto:"+ p); return p; } public static void main(String args[]) { Esercizio8NN esercizio8NN = new Esercizio8NN(); esercizio8NN.start(); } }
-
Esercizio 8.oo)
Creiamo la classe
Esercizio8OO
che rappresenterà un convertitore dalla scala Kelvin alle scale Celsius e Fahrenheit. La nostra applicazione deve chiedere all'utente di inserire un valore in Kelvin, e il programma dovrà convertire il valore inserito in gradi Celsius e Fahrenheit.
Ecco un esempio di output che dovrà generare la nostra applicazione:
Inserisci un valore in Kelvin 0 Il valore di 0.0 gradi Kelvin equivale a: -273.15 gradi Celsius -459.66998 gradi Fahrenheit
Utilizzare il tipofloat
per fare tutti i calcoli.
Ricordiamo che la formula per convertire i gradi Kelvin (che rappresentiamo con "K") in Celsius (che rappresentiamo con "C") è: 0 K - 273,15 = -273,1 C. Ricordiamo che la formula per convertire i gradi Kelvin in Fahrenheit (che rappresentiamo con "F") è: (0 K - 273,15) × 9/5 + 32 = -459,7 F.
Soluzione
La soluzione potrebbe essere la seguente:
import java.util.*; public class Esercizio8OO { public static void main(String[] args) { Esercizio8OO esercizio8OO = new Esercizio8OO(); esercizio8OO.start(); } public void start() { try { Scanner scanner = new Scanner(System.in); System.out.println("Inserisci un valore in Kelvin"); float kelvin = scanner.nextFloat(); float celsius = kelvin - 273.15F; float fahrenheit = celsius * 9/5 + 32; System.out.println("Il valore di " + kelvin + " gradi Kelvin equivale a:\n" + celsius +" gradi Celsius\n" + fahrenheit +" gradi Fahrenheit"); } catch (InputMismatchException exc) { System.out.println("Il valore deve essere numerico"); start(); } } }
-
Esercizio 8.pp)
L'output dell'esercizio precedente non ci soddisfa in quanto l'utilizzo del tipo
float
non ci permette di definire una formattazione univoca. Far evolvere la classeEsercizio8OO
nella classeEsercizio8PP
, utilizzando la classeBigDecimal
in modo tale che i valori vengano formattati specificando solo le prime due cifre decimali. È necessario studiare la documentazione della classeBigDecimal
. Ecco un esempio di output che dovrà generare la nostra applicazione:
Inserisci un valore in Kelvin 0 Il valore di 0 gradi Kelvin equivale a: -273.15 gradi Celsius -459.67 gradi Fahrenheit
Soluzione
La soluzione potrebbe essere la seguente:
import java.util.*; import java.math.*; public class Esercizio8PP { public static void main(String[] args) { Esercizio8PP esercizio8PP = new Esercizio8PP(); esercizio8PP.start(); } public void start() { try { Scanner scanner = new Scanner(System.in); System.out.println("Inserisci un valore in Kelvin"); BigDecimal kelvin = scanner.nextBigDecimal(); BigDecimal celsius = kelvin.subtract(BigDecimal.valueOf(273.15)); BigDecimal fahrenheit = celsius.multiply(BigDecimal.valueOf(9)). divide(BigDecimal.valueOf(5)).add(BigDecimal.valueOf(32)); System.out.println("Il valore di "+ kelvin + " gradi Kelvin equivale a:\n" + celsius +" gradi Celsius\n" + fahrenheit +" gradi Fahrenheit"); } catch (InputMismatchException exc) { System.out.println("Il valore deve essere numerico"); start(); } } }
-
Esercizio 8.qq)
Far evolvere la classe
Esercizio8PP
nella classeEsercizio8QQ
, in modo tale che l'utente possa partire da una qualsiasi scala ed ottenere nelle altre scale.
Ecco un esempio di output che dovrà generare la nostra applicazione:
Inserisci un valore in Kelvin (per esempio 12K), Celsius (25C) o Fahrenheit (451F) 0k Il valore di 0 gradi Kelvin equivale a: -273.15 gradi Celsius -459.67 gradi Fahrenheit
oppure:
Inserisci un valore in Kelvin (per esempio 12K), Celsius (25C) o Fahrenheit (451F) 25C Il valore di 25 gradi Celsius equivale a: 298.15 gradi Kelvin 77.00 gradi Fahrenheit
o anche:
Inserisci un valore in Kelvin (per esempio 12K), Celsius (25C) o Fahrenheit (451F) 500F Il valore di 500 gradi Fahrenheit equivale a: 260.00 gradi Celsius 533.15 gradi Kelvin
Soluzione
La soluzione potrebbe essere la seguente:
import java.util.*; import java.math.*; public class Esercizio8QQ { public static void main(String[] args) { Esercizio8QQ esercizio8QQ = new Esercizio8QQ(); esercizio8QQ.start(); } public void start() { try { System.out.println("Inserisci un valore in Kelvin (per esempio 12K)" + ", Celsius (25C) o Fahrenheit (451F)"); Scanner scanner = new Scanner(System.in); String input = scanner.next(); int lastCharIndex = input.length()-1; BigDecimal valore = new BigDecimal( input.substring(0, lastCharIndex)); String scala = input.substring(lastCharIndex); switch(scala) { case"k": case"K": { BigDecimal celsius = valore.subtract( BigDecimal.valueOf(273.15)); BigDecimal fahrenheit = celsius.multiply(BigDecimal.valueOf(9)) .divide(BigDecimal.valueOf(5), 2, RoundingMode.HALF_UP) .add(BigDecimal.valueOf(32)); System.out.println("Il valore di "+ valore + " gradi Kelvin equivale a:\n" + celsius +" gradi Celsius\n" + fahrenheit +" gradi Fahrenheit"); break; } case"c": case"C": { BigDecimal kelvin = valore.add(BigDecimal.valueOf(273.15)); BigDecimal fahrenheit = valore.multiply(BigDecimal.valueOf(9)) .divide(BigDecimal.valueOf(5),2, RoundingMode.HALF_UP) .add(BigDecimal.valueOf(32)); System.out.println("Il valore di "+ valore + " gradi Celsius equivale a:\n" + kelvin +" gradi Kelvin\n"+ fahrenheit + " gradi Fahrenheit"); break; } case"f": case"F": { BigDecimal celsius = valore.subtract(BigDecimal.valueOf(32)) .multiply(BigDecimal.valueOf(5)) .divide(BigDecimal.valueOf(9), 2, RoundingMode.HALF_UP); BigDecimal kelvin = celsius.add(BigDecimal.valueOf(273.15)); System.out.println("Il valore di "+ valore + " gradi Fahrenheit equivale a:\n" + celsius +" gradi Celsius\n"+ kelvin + " gradi Kelvin"); break; } default: System.out.println("Scala " + scala + " non valida. Usare" + " K per Kelvin, C per Celsius, F per Fahrenheit"); start(); } } catch (NumberFormatException exc) { System.out.println("Formato errato!"); start(); } } }
-
Esercizio 8.rr)
Far evolvere la classe
Esercizio8QQ
nella classeEsercizio8RR
, cercando di migliorare l'astrazione del nostro codice. Creiamo quindi una classeConvertitore
che avrà la responsabilità di eseguire tutte le conversioni aritmetiche. Dichiarerà quindi tutti i metodi (statici) che servono per convertire una scala in un'altra. Per esempio ecco la dichiarazione del metodo di conversione da Kelvin a Fahrenheit:
public static BigDecimal convertiKelvinInFahrenheit(BigDecimal kelvin) { //scrivi il tuo codice qui }
Ovviamente la classe
Esercizio8RR
dovrà ora utilizzare i metodi della classeConvertitore
.
Soluzione
La classe
Convertitore
potrebbe essere implementata nel seguente modo:
import java.math.*; public class Convertitore { public static BigDecimal convertiKelvinInCelsius(BigDecimal kelvin) { return kelvin.subtract(BigDecimal.valueOf(273.15)); } public static BigDecimal convertiKelvinInFahrenheit(BigDecimal kelvin) { BigDecimal celsius = convertiKelvinInCelsius(kelvin); return celsius.multiply(BigDecimal.valueOf(9)) .divide(BigDecimal.valueOf(5), 2, RoundingMode.HALF_UP) .add(BigDecimal.valueOf(32)); } public static BigDecimal convertiCelsiusInKelvin(BigDecimal celsius) { return celsius.add(BigDecimal.valueOf(273.15)); } public static BigDecimal convertiCelsiusInFahrenheit(BigDecimal celsius) { return celsius.multiply(BigDecimal.valueOf(9)) .divide(BigDecimal.valueOf(5),2, RoundingMode.HALF_UP) .add(BigDecimal.valueOf(32)); } public static BigDecimal convertiFahrenheitInKelvin(BigDecimal fahrenheit) { BigDecimal celsius = convertiFahrenheitInCelsius(fahrenheit); return celsius.add(BigDecimal.valueOf(273.15)); } public static BigDecimal convertiFahrenheitInCelsius(BigDecimal fahrenheit){ return fahrenheit.subtract(BigDecimal.valueOf(32)) .multiply(BigDecimal.valueOf(5)) .divide(BigDecimal.valueOf(9), 2, RoundingMode.HALF_UP); } }
Quindi la classeEsercizio8RR
, potrebbe essere implementata nel seguente modo:
import java.util.*; import java.math.*; public class Esercizio8RR { public static void main(String[] args) { Esercizio8RR esercizio8RR = new Esercizio8RR(); esercizio8RR.start(); } public void start() { try { System.out.println("Inserisci un valore in Kelvin (per esempio 12K)" + ", Celsius (25C) o Fahrenheit (451F)"); Scanner scanner = new Scanner(System.in); String input = scanner.next(); int lastCharIndex = input.length()-1; BigDecimal valore = new BigDecimal( input.substring(0, lastCharIndex)); String scala = input.substring(lastCharIndex); switch(scala) { case"k": case"K": { BigDecimal celsius = Convertitore.convertiKelvinInCelsius(valore); BigDecimal fahrenheit = Convertitore.convertiKelvinInFahrenheit(valore); System.out.println("Il valore di "+ valore + " gradi Kelvin equivale a:\n" + celsius +" gradi Celsius\n" + fahrenheit +" gradi Fahrenheit"); break; } case"c": case"C": { BigDecimal kelvin = Convertitore.convertiCelsiusInKelvin(valore); BigDecimal fahrenheit = Convertitore.convertiCelsiusInFahrenheit(valore); System.out.println("Il valore di "+ valore + " gradi Celsius equivale a:\n" + kelvin +" gradi Kelvin\n"+ fahrenheit + " gradi Fahrenheit"); break; } case"f": case"F": { BigDecimal celsius = Convertitore.convertiFahrenheitInCelsius(valore); BigDecimal kelvin = Convertitore.convertiFahrenheitInKelvin(valore); System.out.println("Il valore di "+ valore + " gradi Fahrenheit equivale a:\n" + celsius +" gradi Celsius\n"+ kelvin + " gradi Kelvin"); break; } default: System.out.println("Scala "+ scala +" non valida. Usare K per" + " Kelvin, C per Celsius, F per Fahrenheit"); start(); } } catch (NumberFormatException exc) { System.out.println("Formato errato!"); start(); } } }
-
Esercizio 8.ss)
Far evolvere la classe
Esercizio8RR
nella classeEsercizio8SS
, cercando di migliorare l'astrazione del nostro codice. Creiamo quindi una classeStampante
che avrà la responsabilità di stampare tutti i messaggi. Dichiarerà quindi tutti i metodi (statici) che servono per stampare i messaggi. Per esempio ecco la dichiarazione del metodo che stampa il messaggio di errore quando si inserisce un simbolo non valido per scegliere la scala del valore inserito:
public static void stampaScalaErrata(String scala) { //scrivi il tuo codice qui }
Ovviamente la classe
Esercizio8SS
dovrà ora utilizzare i metodi della classeStampante
.
Soluzione
La classe
Stampante
potrebbe essere implementata nel seguente modo:
import java.math.*; public class Stampante { public static void stampaIstruzioni() { System.out.println("Inserisci un valore in Kelvin (per esempio 12K)," + " Celsius (25C) o Fahrenheit (451F)"); } public static void stampaMessaggio(BigDecimal valore1, String scala1,BigDecimal valore2, String scala2,BigDecimal valore3, String scala3) { System.out.println("Il valore di "+ valore1 +" gradi " + scala1 + " equivale a:\n" + valore2 +" gradi " + scala2 + "\n" + valore3 + " gradi " + scala3); } public static void stampaScalaErrata(String scala) { System.out.println("Scala "+ scala +" non valida. Usare K per Kelvin, " + "C per Celsius, F per Fahrenheit"); } public static void stampaFormatoErrato() { System.out.println("Formato errato!"); } }
Quindi la classeEsercizio8SS
, potrebbe essere implementata nel seguente modo:
import java.util.*; import java.math.*; public class Esercizio8SS { private static final String K = "Kelvin"; private static final String C = "Celsius"; private static final String F = "Fahrenheit"; public static void main(String[] args) { Esercizio8SS esercizio8SS = new Esercizio8SS(); esercizio8SS.start(); } public void start() { try { Stampante.stampaIstruzioni(); Scanner scanner = new Scanner(System.in); String input = scanner.next(); int lastCharIndex = input.length()-1; BigDecimal valore = new BigDecimal( input.substring(0, lastCharIndex)); String scala = input.substring(lastCharIndex); switch(scala) { case"k": case"K": { BigDecimal celsius = Convertitore.convertiKelvinInCelsius(valore); BigDecimal fahrenheit = Convertitore.convertiKelvinInFahrenheit(valore); Stampante.stampaMessaggio(valore, K, celsius, C, fahrenheit, F); break; } case"c": case"C": { BigDecimal kelvin = Convertitore.convertiCelsiusInKelvin(valore); BigDecimal fahrenheit = Convertitore.convertiCelsiusInFahrenheit(valore); Stampante.stampaMessaggio(valore, C, kelvin, K, fahrenheit, F); break; } case"f": case"F": { BigDecimal celsius = Convertitore.convertiFahrenheitInCelsius(valore); BigDecimal kelvin = Convertitore.convertiFahrenheitInKelvin(valore); Stampante.stampaMessaggio(valore, F, celsius, C, kelvin, K); break; } default: Stampante.stampaScalaErrata(scala); start(); } } catch (NumberFormatException exc) { Stampante.stampaFormatoErrato(); start(); } } }
Notare che il metodostampaMessaggio
ha indubbiamente troppi parametri, ed il suo utilizzo sembra risultare abbastanza complesso. Tuttavia la soluzione sembra migliore rispetto a quella del precedente esercizio. -
Esercizio 8.tt)
In questo esercizio proveremo ad utilizzare per la prima volta la libreria
java.logging
, in realtà introdotta solo nell'approfondimento 14.1, che potete anche già consultare. A differenza di quanto visto nell'approfondimento 14.1, andremo a configurare il nostro log tramite il seguente file di configurazione (che trovate nella cartella del 8.tt del codice del libro):
handlers=java.util.logging.FileHandler .level=ALL java.util.logging.FileHandler.pattern=./esercizio8tt.log java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter Esercizio8TT.level=ALL
Dove viene specificato che il log verrà generato all'interno di un file nella stessa cartella chiamato
esercizio8tt.log
. Inoltre viene specificato che verranno stampati tutti i messaggi di log a qualsiasi livello (istruzioneEsercizio8TT.level=ALL
). Inserire solo all'interno del fileEsercizio8TT
(evoluzione del fileEsercizio8SS
) il seguente metodo:
private static void initLogging() { try { logManager.readConfiguration( new FileInputStream("logging.properties")); } catch (IOException exception) { LOGGER.log(Level.SEVERE, "Problema con la lettura del file di configurazione",exception); } }
e richiamarlo come prima istruzione del metodo
main
, affinché la configurazione sia tramite file sia correttamente eseguita. Inoltre inserire a proprio piacimento alcune istruzioni di log con livelli diversi. Testare che funzioni tutto correttamente utilizzando l'applicazione e controllando il file di log. Testare più volte l'applicazione specificando nel file di configurazione, al posto della parolaALL
altri livelli di configurazione comeSEVERE
,INFO
etc. verificando che vengano stampati solo i messaggi con i livelli di log consentiti dalla configurazione.
Soluzione
La seguente potrebbe essere una possibile implementazione della classe
Esercizio8TT
:
import java.util.*; import java.io.*; import java.math.*; import java.util.logging.*; public class Esercizio8TT { private static final LogManager logManager = LogManager.getLogManager(); private static final Logger LOGGER = Logger.getLogger("Esercizio8TT"); private static final String K ="Kelvin"; private static final String C ="Celsius"; private static final String F ="Fahrenheit"; public static void main(String[] args) { initLogging(); LOGGER.info("Metodo main() chiamato"); Esercizio8TT esercizio8TT = new Esercizio8TT(); esercizio8TT.start(); LOGGER.info("Programma terminato"); } private static void initLogging() { try { logManager.readConfiguration( new FileInputStream("logging.properties")); } catch (IOException exception) { LOGGER.log(Level.SEVERE, "Problema con la lettura del file di configurazione",exception); } } public void start() { try { LOGGER.info("Metodo start() chiamato"); Stampante.stampaIstruzioni(); Scanner scanner = new Scanner(System.in); String input = scanner.next(); int lastCharIndex = input.length()-1; BigDecimal valore = new BigDecimal(input.substring(0, lastCharIndex)); String scala = input.substring(lastCharIndex); switch(scala) { case"k": case"K": { LOGGER.fine("Inserito scala Kelvin"); BigDecimal celsius = Convertitore.convertiKelvinInCelsius(valore); BigDecimal fahrenheit = Convertitore.convertiKelvinInFahrenheit(valore); Stampante.stampaMessaggio(valore, K, celsius, C, fahrenheit, F); break; } case"c": case"C": { LOGGER.fine("Inserito scala Celsius"); BigDecimal kelvin = Convertitore.convertiCelsiusInKelvin(valore); BigDecimal fahrenheit = Convertitore.convertiCelsiusInFahrenheit(valore); Stampante.stampaMessaggio(valore, C, kelvin, K, fahrenheit, F); break; } case"f": case"F": { LOGGER.fine("Inserito scala Fahrenheit"); BigDecimal celsius = Convertitore.convertiFahrenheitInCelsius(valore); BigDecimal kelvin = Convertitore.convertiFahrenheitInKelvin(valore); Stampante.stampaMessaggio(valore, F, celsius, C, kelvin, K); break; } default: LOGGER.fine("Inserito scala errata"); Stampante.stampaScalaErrata(scala); start(); } } catch (NumberFormatException exc) { LOGGER.severe(exc.getMessage()); Stampante.stampaFormatoErrato(); start(); } } }
Lanciando questa classe con il livello di defaultALL
, nel file di log troveremo il seguente testo:
apr 27, 2020 12:53:01 PM Esercizio8TT main INFO: Metodo main() chiamato apr 27, 2020 12:53:01 PM Esercizio8TT start INFO: Metodo start() chiamato apr 27, 2020 12:53:03 PM Esercizio8TT start FINE: Inserito scala Kelvin apr 27, 2020 12:53:03 PM Esercizio8TT main INFO: Programma terminato
Ma modificando il livello di log adINFO
, ed utilizzando l'applicazione correttamente, troveremo questo testo nel file di log:
apr 27, 2020 12:54:01 PM Esercizio8TT main INFO: Metodo main() chiamato apr 27, 2020 12:54:02 PM Esercizio8TT start INFO: Metodo start() chiamato apr 27, 2020 12:54:05 PM Esercizio8TT main INFO: Programma terminato
Ovvero sono stati ignorati i log di tipoFINE
.
Infine se modificassimo il livello di log aSEVERE
vedremmo solo i log di livelloSEVERE
. Quindi il file risulterebbe non modificato se l'applicazione fosse usata correttamente, mentre conterrebbe i messaggi di log definiti per gestire l'eccezione nel caso l'utente specifichi una scala non valida. Per esempio utilizzando l'applicazione nel seguente modo:
Inserisci un valore in Kelvin (per esempio 12K), Celsius (25C) o Fahrenheit (451F) j Formato errato! Inserisci un valore in Kelvin (per esempio 12K), Celsius (25C) o Fahrenheit (451F) 0k Il valore di 0 gradi Kelvin equivale a: -273.15 gradi Celsius -459.67 gradi Fahrenheit
Otterremmo il seguente file di log:
apr 27, 2020 12:56:21 PM Esercizio8TT start SEVERE: Character j is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
-
Esercizio 8.uu)
Ore implementeremo un semplice gioco dopo tutta questa matematica e fisica! La nostra applicazione chiederà all'utente di indovinare un numero casuale compreso tra 1 e 100 contando il numero di tentativi.
Un esempio dell'output che dovrà essere generato da questa applicazione è il seguente:
Ho pensato ad un numero tra 1 e 100, indovinalo! 50 Troppo alto, riprova 25 Troppo basso, riprova 37 Troppo basso, riprova 42 Troppo basso, riprova 48 Troppo basso, riprova 49 Complimenti! Hai indovinato dopo 6 tentativi
Proviamo a scrivere le nostre classi in inglese (in fondo ci dobbiamo abituare), e creare la classe
GuessNumber
che contiene un unico metodo statico che genera il numero casuale tra 1 e il numero massimo che gli viene passato in input. Ecco la dichiarazione del metodo:
public static int generateRandomNumber(int max) { //scrivi il tuo codice qui }
Poi, data la seguente interfaccia:
public interface Game { public void init(); public void start(); public void play(); public void end(); }
implementarla in una classe chiamata
GuessNumberGame
, che implementa i metodi dell'interfaccia nel modo più opportuno. Nella stessa classe possiamo anche mettere il metodomain
che farà partire l'applicazione.
Soluzione
Possiamo implementare la classe
GuessNumber
nel seguente modo:
import java.util.Random; public class GuessNumber { private static final Random RANDOM = new Random(); public static int generateRandomNumber(int max) { return 1+ RANDOM.nextInt(max); } }
Mentre possiamo definire la classeGuessNumberGame
come segue:
import java.util.Scanner; import java.util.Random; public class GuessNumberGame implements Game { private static final int MAX_NUMBER = 100; private Scanner scanner; private int attemptsNumber; private int numberToGuess; public GuessNumberGame () { init(); start(); play(); end(); } @Override public void init() { scanner = new Scanner(System.in); numberToGuess = GuessNumber.generateRandomNumber(MAX_NUMBER); } @Override public void start() { System.out.println("Ho pensato ad un numero tra 1 e " + MAX_NUMBER +", indovinalo!"); } @Override public void play() { attemptsNumber++; int number = scanner.nextInt(); if (number < numberToGuess) { System.out.println("Troppo basso, riprova"); } else if (number > numberToGuess) { System.out.println("Troppo alto, riprova"); } else { return; } play(); } @Override public void end() { System.out.println("Complimenti! Hai indovinato dopo " + attemptsNumber + " tentativi"); } public static void main(String args[]) { new GuessNumberGame(); } }
Notare che il metodomain
ha semplicemente istanziato l'oggettoGuessNumberGame
chiamandone il costruttore. Quest'ultimo ha invocato i metodi definiti dall'interfaccia che sono stati implementati in maniera molto semplice e coerente. Merita attenzione solo il metodo ricorsivoplay
, che richiama sé stesso sin quando l'utente non ha indovinato il numero. -
Esercizio 8.vv)
La soluzione dell'esercizio precedente, non teneva conto però della possibilità che l'utente inserisca dati non validi. Quindi far evolvere la classe
GuessNumberGame
, in modo tale da gestire un eventuale input scorretto da parte dell'utente.
Un esempio dell'output che dovrà essere generato da questa applicazione è il seguente:
Ho pensato ad un numero tra 1 e 100, indovinalo! u Input 'u' non valido. Puoi inserire solo numeri interi, riprova
Soluzione
Possiamo ridefinire la classe
GuessNumberGame
come segue, rimuovendo la natura ricorsiva del metodoplay
:
import java.util.Scanner; import java.util.Random; public class GuessNumberGame implements Game { private static final int MAX_NUMBER = 100; private Scanner scanner; private int attemptsNumber; private int numberToGuess; public GuessNumberGame () { init(); start(); play(); end(); } @Override public void init() { scanner = new Scanner(System.in); numberToGuess = GuessNumber.generateRandomNumber(MAX_NUMBER); } @Override public void start() { System.out.println("Ho pensato ad un numero tra 1 e " + MAX_NUMBER +", indovinalo!"); } @Override public void play() { while (scanner.hasNext()) { if (scanner.hasNextInt()) { attemptsNumber++; int number = scanner.nextInt(); if (number < numberToGuess) { System.out.println("Troppo basso, riprova"); } else if (number > numberToGuess) { System.out.println("Troppo alto, riprova"); } else { return; } //play() } else { System.out.println("Input '"+ scanner.next() +"' non valido. Puoi inserire solo numeri interi, riprova"); } } } @Override public void end() { System.out.println("Complimenti! Hai indovinato dopo " + attemptsNumber + " tentativi"); } public static void main(String args[]) { new GuessNumberGame(); } }
-
Esercizio 8.ww)
Iniziamo a modificare la nostra applicazione per renderla un gioco multi-utente. Creiamo quindi la classe
Player
che deve definire:
• le variabili d'istanzaname
eid
(che servirà per identificare univocamente un giocatore anche in caso di coincidenza di nomi).
• Un unico costruttore.
• Un semplice metodotoString
Definire una classe astratta
MultiPlayerGame
(gioco multi-utente) che implementa l'interfacciaGame
definita nell'esercizio 8.uu, e che definisce:
• come variabile d'istanza unArrayList
di oggettiPlayer
chiamatoplayers
.
• Il metodo gettergetPlayers
, che restituisce la variabile d'istanzaplayers
.
• I metodiaddPlayer
eremovePlayer
che aggiungono o rimuovono un oggetto di tipoPlayer
dalla variabile d'istanzaplayers
.
• Un metodogetPlayer
che prende in input l'indice delplayer
all'interno della listaplayers
, e che ritorna l'oggettoPlayer
corrispondente, oppure rilancia unaPlayerException
(da creare anch'essa), nei casi l'indice non sia valido.
• Un metodogetPlayer
che prende in input il nome di un giocatore presente all'interno della listaplayers
, e che ritorna l'oggettoPlayer
corrispondente, oppure rilancia unaPlayerException
, nei casi in cui, non venga trovato ilplayer
con il nome specificato, o se sono presenti più giocatori con lo stessonome
all'interno della lista.
• Creare una classe concretaMultiPlayerGameImpl
("Impl" sta per implementazione) che estendeMultiPlayerGame
, e definisce un nuovo metodoprintPlayers
che stampa i dettagli di tutti i giocatori della listaplayers
.
• Infine creare una classe di testMultiPlayerGameTest
che istanzia un oggetto daMultiPlayerGameImpl
, e tutti testa tutti i metodi definiti daMultiPlayerGame
.
Soluzione
Possiamo implementare la classe
Player
nel seguente modo:
public class Player { private int id; private String name; public Player (int id, String name) { this.setId(id); this.setName(name); } public void setId(int id) { this.id = id; } public int getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "Player " + id + ": " + name; } }
Poi implementiamo l'eccezionePlayerException
:
public class PlayerException extends Exception { public PlayerException (String message){ super("Eccezione riguardante il player: " + message); } }
Segue l'implementazione della classe astrattaMultiPlayerGame
:
import java.util.List; import java.util.ArrayList; public abstract class MultiPlayerGame implements Game { private List<Player> players; public MultiPlayerGame () { init(); start(); play(); end(); } @Override public void init() { players = new ArrayList<>(); } public void addPlayer(Player player) { players.add(player); } public void removePlayer(Player player) { players.add(player); } public Player getPlayer(int id) throws PlayerException { if (id < 0) { throw new PlayerException("L'identificativo del player non può" + "essere negativo"); } else if (id >=players.size()) { throw new PlayerException("L'identificativo del player non esiste"); } return players.get(id); } public Player getPlayer(String name) throws PlayerException { Player playerFound = null; int counter = 0; for (Player player : players) { if (player.getName().equals(name)) { counter++; playerFound = player; } } if (counter == 0) { throw new PlayerException("Player con nome "+ name +" non trovato"); } else if (counter > 1) { throw new PlayerException("Trovati "+ counter +" player con nome " + name); } return playerFound; } public List<Player> getPlayers() { return players; } }
Poi abbiamo definito in maniera minimale la sua implementazione:
import java.util.Iterator; public class MultiPlayerGameImpl extends MultiPlayerGame { public void start() { // per ora nessuna implementazione } public void play() { // per ora nessuna implementazione } public void end() { // per ora nessuna implementazione } public void printPlayers() { System.out.println("Lista dei giocatori:"); for (Player player : getPlayers()) { System.out.println(player); } } }
Infine segue la nostra classe di test:
public class MultiPlayerGameTest { private static int counter; public static void main(String args[]) { MultiPlayerGameImpl game = new MultiPlayerGameImpl(); Player p1 = new Player(++counter,"John"); Player p2 = new Player(++counter,"James"); game.addPlayer(p1); game.addPlayer(p2); game.printPlayers(); System.out.println(); //NAME OK try { System.out.println("Recuperato player: "+ game.getPlayer("John")); } catch (Exception exc) { assert false; System.out.println(exc.getMessage()); } //NAME NOT OK try { System.out.println("Recuperato player: "+ game.getPlayer("Jack")); assert false; } catch (Exception exc) { System.out.println(exc.getMessage()); } try { Player p3 = new Player(++counter,"John"); game.addPlayer(p3); game.printPlayers(); System.out.println(); System.out.println("Recuperato player: "+game.getPlayer("John")); assert false; } catch (Exception exc) { System.out.println(exc.getMessage()); } //ID OK try { System.out.println("Recuperato player: "+game.getPlayer(2)); } catch (Exception exc) { assert false; System.out.println(exc.getMessage()); } //ID NOT OK try { System.out.println("Recuperato player: "+game.getPlayer(-1)); assert false; } catch (Exception exc) { System.out.println(exc.getMessage()); } try { System.out.println("Recuperato player: "+game.getPlayer(3)); assert false; } catch (Exception exc) { System.out.println(exc.getMessage()); } } }
che produce il seguente output:
Lista dei giocatori: Player 1: John Player 2: James Recuperato player: Player 1: John Eccezione riguardante il player: Player con nome Jack non trovato Lista dei giocatori: Player 1: John Player 2: James Player 3: John Eccezione riguardante il player: Trovati 2 player con nome John Recuperato player: Player 3: John Eccezione riguardante il player: L'identificativo del player non può essere negativo Eccezione riguardante il player: L'identificativo del player non esiste
-
Esercizio 8.xx)
Considerando le classi implementate nella soluzione dell'esercizio 8.ww (escludendo
MultiPlayerGameImpl
), e la seguente interfaccia:
public interface Configurator { public void config(); }
L'obiettivo di questo esercizio, e di creare una implementazione dell'interfacciaConfigurator
chiamataMultiPlayerGameConfigurator
. Questa classe deve avere la responsabilità di permettere la configurazione di un gioco di diventare multi-giocatore. In particolare deve permettere di inserire i nomi dei giocatori che devono partecipare al gioco, assegnando ad ognuno di essi un id progressivo. Questa classe deve dichiarare come variabile d'istanza quantomeno un oggetto di tipoMultiPlayerGameImpl
, a cui devono essere aggiunti i giocatori.
Lanciando quindi la seguente classe:
public class MultiPlayerGameConfiguratorTest { public static void main(String args[]) { new MultiPlayerGameConfigurator(); } }
vogliamo che l'output del nostro programma sia simile al seguente:
Inserire nome player 1 Claudio Inserire nome player 2 oppure 'i' per iniziare a giocare Pippo Inserire nome player 3 oppure 'i' per iniziare a giocare i Lista dei giocatori: Player 1: Claudio Player 2: Pippo
Soluzione
Una possibile soluzione è rappresentata dal seguente codice:
import java.util.Scanner; public class MultiPlayerGameConfigurator implements Configurator { private static int counter; private Scanner scanner; private MultiPlayerGameImpl game; public MultiPlayerGameConfigurator() { game = new MultiPlayerGameImpl(); scanner = new Scanner(System.in); config(); game.printPlayers(); } @Override public void config() { var players = game.getPlayers(); System.out.println("Inserire nome player "+ (++counter) + (players.isEmpty() ?"":" oppure 'i' per iniziare a giocare")); String text = scanner.next(); if (!players.isEmpty() && text.equals("i")) { return; } game.addPlayer(new Player (counter, text)); config(); } }
Notare l'implementazione ricorsiva del metodoconfig
, che ci permette di aggiungere quanti giocatori vogliamo. -
Esercizio 8.yy)
Ora, dopo aver realizzato delle implementazioni delle interfacce
MultiPlayer
eConfigurator
, non ci resta che unire il tutto e far evolvere la nostra applicazione definita nell'esercizio 8.vv in un gioco multi-utente e configurabile. Quindi per prima cosa, rendiamo la classePlayer
astratta, aggiungendogli il seguente metodo astrattoplay
:
public abstract boolean play();
d'altronde un giocatore che non definisce un metodo
play
non ha molto senso.
Quindi definire l'implementazione della classePlayer
per il nostro gioco, che chiameremoGuessNumberPlayer
. Essa deve avere la responsabilità di:
• contare il numero di tentativi fatti per indovinare il numero.
• Implementare il metodoplay
sulla falsariga di quanto faceva prima la classeGuessNumberGame
. Infatti ora sarà il giocatore a giocare, e quindi deve implementare il codice che gli permetta di giocare. Il metodoplay
deve ritornaretrue
, solo quando il giocatore indovina il numero.Inoltre bisogna ridefinire la classe
GuessNumberGame
, in modo tale che implementi l'interfacciaConfigurator
, e gestisca il gioco in maniera multi-utente utilizzando oggetti di tipoGuessNumberPlayer
. In particolare essa deve:
• ridefinire il metodoconfig
per gestire la configurazione dell'applicazione.
• Ridefinire il metodoplay
in modo che sfrutti il metodoplay
degli oggetti di tipoGuessNumberGame
.
Soluzione
Per completezza, riportiamo la classe
Player
che è stata resa astratta e modificata con l'aggiunta del metodo astrattoplay
:
public abstract class Player { private int id; private String name; public Player(int id, String name) { this.setId(id); this.setName(name); } public abstract boolean play(); public void setId(int id) { this.id = id; } public int getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "Player " + id + ": " + name; } }
segue invece l'implementazione della classeGuessNumberPlayer
che definisce la logica per giocare nel suo metodoplay
, molto simile a quella che era la logica nella versione della classeGuessNumberGame
dell'esercizio 8.vv:
import java.util.Scanner; public class GuessNumberPlayer extends Player { private Scanner scanner; private int numberToGuess; private int attemptsNumber; public GuessNumberPlayer (int id, String name, Scanner scanner, int numberToGuess) { super(id, name); this.scanner = scanner; this.numberToGuess = numberToGuess; } public void incrementAttemptsNumber() { attemptsNumber++; } public int getAttemptsNumber() { return attemptsNumber; } @Override public boolean play() { if (scanner.hasNextInt()) { incrementAttemptsNumber(); int number = scanner.nextInt(); if (number < numberToGuess) { System.out.println("Troppo basso, riprova"); } else if (number > numberToGuess) { System.out.println("Troppo alto, riprova"); } else { return true; } } else { System.out.println("Input '"+ scanner.next() + "' non valido. Puoi inserire solo numeri interi, riprova"); play(); } return false; } }
infine, di seguito è riportata la nostra implementazione della nuova classeGuessNumberGame
:
import java.util.Scanner; import java.util.Random; import java.util.List; import java.util.ArrayList; public class GuessNumberGame extends MultiPlayerGame implements Configurator { private static final int MAX_NUMBER = 100; private static int counter; private Player currentPlayer; private Scanner scanner; // private int attemptsNumber; private int numberToGuess; /* public GuessNumberGame () { init(); start(); play(); end(); }*/ @Override public void init() { super.init(); scanner = new Scanner(System.in); numberToGuess = GuessNumber.generateRandomNumber(MAX_NUMBER); } @Override public void start() { config(); System.out.println("Ho pensato ad un numero tra 1 e " + MAX_NUMBER +", indovinalo!"); } @Override public void config() { System.out.println("Inserire nome player "+ (++counter) +(getPlayers().isEmpty() ?"":" oppure 'i' per iniziare a giocare")); String text = scanner.next(); if (!getPlayers().isEmpty() && text.equals("i")) { currentPlayer = getPlayers().get(0); System.out.println("Tocca a "+ currentPlayer); return; } addPlayer(new GuessNumberPlayer (counter, text, scanner,numberToGuess)); config(); } @Override public void play() { while (scanner.hasNext()) { boolean result = currentPlayer.play(); if (result) { return; } currentPlayer = getCurrentPlayer(); System.out.println("Tocca a "+ currentPlayer); } } private Player getCurrentPlayer() { int nextPlayerIndex = currentPlayer.getId(); var players = getPlayers(); if (nextPlayerIndex == players.size()) { nextPlayerIndex = 0; } return players.get(nextPlayerIndex); } @Override public void end() { System.out.println("Complimenti "+ currentPlayer.getName() + "! Hai indovinato dopo " + ((GuessNumberPlayer)currentPlayer) .getAttemptsNumber() +" tentativi"); } public static void main(String args[]) { new GuessNumberGame(); } }
-
Esercizio 8.zz)
In questo ultimo esercizio, l'unica richiesta è quella di inserire ogni classe in un package con un nome significativo, in modo da rendere evidente la struttura architetturale della nostra applicazione. Di seguito vengono elencati tutti i package dell'applicazione:
•games.generic.exceptions
•games.generic.data
•games.guessnumber.data
•games.guessnumber.business
•games.guessnumber.util
•games.generic.business
•games.generic.business
il vostro compito è quindi quello di assegnare il package giusto alla classe (o interfaccia giusta).
Soluzione
La soluzione dovrebbe essere:
Agames.generic.exceptions
appartiene la classePlayerException
.
Agames.generic.data
appartiene la classePlayer
eMultiPlayerGame
.
Agames.guessnumber.data
appartiene la classeGuessNumberPlayer
.
Agames.guessnumber.business
appartiene la classeGuessNumberGame
.
Agames.guessnumber.util
appartiene la classeGuessNumber
.
Agames.generic.business
appartiene la classeGame
.
Agames.generic.business
appartiene la classeConfigurator
.
Notare che tutte le classi appartenenti al packagegames.generic
, sono classi che potrebbero essere riutilizzate in altri giochi. Quindi in pratica abbiamo creato, quello che in architettura è detto framework, diciamo un "mini-framework", che possiamo riutilizzare per creare nuovi giochi configurabili e multi-utente.