Esercizi
- Home
- Esercizi del capitolo 15 e dell'appendice I
Esercizi del capitolo 15 e dell'appendice I
Stream API e Framework
Collections
Gli esercizi di questo capitolo saranno inizialmente orientati alla teoria, con tre esercizi
di tipo "vero-falso". Poi riprenderemo l'esercizio della rubrica che avevamo realizzato nei primi
capitoli aggiungendo la serializzazione degli oggetti passo dopo passo. Creeremo anche una
interfaccia da riga di comando sfruttando il pattern MVC, arrivando a creare un grado di complessità
notevole, anche per il programmatore già esperto. Come sempre poi troveremo alcuni esercizi
propedeutici alla certificazione da programmatore Oracle. Infine ci sarà anche qualche esercizio per
gli altri argomenti come il networking, e quelli riguardanti la Date-Time API descritta
nell'appendice I (questi ultimi hanno una numerazione diversa che inizia con la lettera I).
Utilizzare la documentazione, anche per svolgere questi esercizi, è fondamentale.
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 15.a) Input - Output, Vero o Falso:
1. Il pattern Decorator permette di implementare una sorta di ereditarietà dinamica. Questo significa che, invece di creare tante classi quante sono le entità da astrarre, al runtime sarà possibile concretizzare uno di questi concetti direttamente con un oggetto.
2. I reader e i writer permettono di leggere e scrivere caratteri. Per tale ragione sono detti "character stream".
3. All'interno del packagejava.io
l'interfacciaReader
ha il ruolo diConcreteComponent
.
4. All'interno del packagejava.io
l'interfacciaInputStream
ha il ruolo diConcreteDecorator
.
5. UnBufferedWriter
è unConcreteDecorator
.
6. Gli stream che possono realizzare una comunicazione direttamente con una fonte o una destinazione vengono detti "node stream".
7. I node stream di tipoOutputStream
possono utilizzare il metodo:per scrivere su una destinazione.int write(byte cbuf[])
8. Il seguente oggetto in:BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
permette di usufruire di un metodo
9. Il seguente codice:readLine
che leggerà frasi scritte con la tastiera delimitate dalla battitura del tasto "Invio".File outputFile = new File("pippo.txt");
crea un file di testo chiamato
pippo.txt
nella cartella corrente.
10. Non è possibile decorare unFileReader
.
Soluzione
1. Vero.
2. Vero.
3. Falso.
4. Falso.
5. Vero.
6. Vero.
7. Vero.
8. Vero.
9. Falso.
10. Falso. -
Esercizio 15.b) Serializzazione, Vero o Falso:
1. Lo stato di un oggetto è definito dal valore delle sue variabili d'istanza (in un certo istante).
2. L'interfacciaSerializable
non ha metodi.
3.transient
è un modificatore applicabile a variabili e classi. Una variabiletransient
non viene serializzata con le altre variabili; una classetransient
non è serializzabile.
4.transient
è un modificatore applicabile a metodi e variabili. Una variabiletransient
non viene serializzata con le altre variabili; un metodo transient non è serializzabile.
5. Se si provasse a serializzare un oggetto che possiede tra le sue variabili d'istanza una variabile di tipoReader
dichiaratatransient
, otterremmo unNotSerializableException
durante l'esecuzione.
6. Una variabilestatic
non sarà serializzata.
7. Una variabile d'istanza di tipoOuputStream
deve essere dichiaratatransient
affinché sia coinvolta in una serializzazione.
8. Non è possibile usare come parametro di untry
-with-resources" un'implementazione diSerializable
.
9. È possibile creare un clone di un oggettotransient
con metodi della libreria input-output.
10. Un oggetto può essere serializzato anche in Base64.
Soluzione
1. Vero.
2. Vero.
3. Falso.
4. Falso.
5. Falso.
6. Vero.
7. Vero.
8. Falso.
9. Vero.
10. Vero. -
Esercizio 15.c) New Input Output, Vero o Falso:
1. NIO 2.0 sostituisce del tutto il modello di input output definito con il package
java.io
.
2. L'utilizzo della classeFile
potrebbe essere completamente rimpiazzato dall'utilizzo della classeFiles
e dell'interfacciaPath
.
3. Il metodotoPath
della classeFile
restituisce l'oggettoPath
equivalente.
4.Path
è un'interfaccia perché la sua implementazione dipende dalla piattaforma.
5. Il metodorelativize
appartiene alla classeFiles
e restituisce il percorso per arrivare dalPath
specificato come primo argomento alPath
specificato come secondo argomento.
6. Il metodosubPath
dell'interfacciaPath
non restituisce il nodo radice.
7. Il metododelete
dell'interfacciaPath
solleverà un'eccezione nel caso si tenti di eliminare una directory non vuota.
8. Non ha senso ottenereReader
oWriter
da un oggettoFiles
.
9. Il nome del file temporaneo è sempre stabilito dal sistema operativo.
10. Un file temporaneo viene salvato in una directory che dipende dal sistema operativo.
Soluzione
1. Vero.
2. Vero.
3. Vero.
4. Vero.
5. Falso.
6. Vero.
7. Vero.
8. Falso.
9. Falso.
10. Vero. -
Esercizio 15.d)
Creare una classe
EditorInterattivo
contenente un metodomain
che permetta di scrivere in un file quello che si scrive da riga di comando.
Soluzione
Il listato dovrebbe essere simile al seguente:
package com.claudiodesio.io; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Scanner; public class EditorInterattivo { public static void main(String args[]) { File file = new File("file.txt"); try (Scanner scanner = new Scanner(System.in); FileWriter fileWriter = new FileWriter(file);) { String stringa = ""; System.out.println("Digita qualcosa e batti enter, oppure scrivi " + "\"fine\" per terminare il programma"); while (!(stringa = scanner.nextLine()).equals("fine")) { System.out.println("Hai digitato " + stringa); fileWriter.append(stringa); fileWriter.flush(); } } catch (IOException ex) { ex.printStackTrace(); } System.out.println("Fine programma!"); } }
-
Esercizio 15.e)
Creare una classe
More
contenente un metodomain
che simuli il famoso programmamore
che troviamo nei sei sistemi Linux. L'applicazione, una volta eseguita, aspetterà che l'utente specifichi il file da leggere in input. Dopo aver caricato il file specificato dall'utente, il programma rimarrà in attesa di un ulteriore input da parte dell'utente. Se viene premuto il tasto "n" seguito dal tasto "invio", allora verranno visualizzate le prime 10 righe del file caricato, ed ogni volta che viene ripetuta l'azione il programma dovrà stampare le successive 10 righe, fino a quando termineranno le righe del file. Specificando però la lettera "q" seguita da "invio" il programma dovrà terminare (anche se non sono state lette tutte le righe).
Soluzione
Il listato dovrebbe essere simile al seguente:
package com.claudiodesio.io; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class More { private final static int numeroRighe = 10; private static final String QUIT_COMMAND = "q"; private static final String NEXT_COMMAND = "n"; public static void main(String args[]) { String carattere; System.out.println("Digita il nome di un file e batti enter, oppure " + "scrivi \"" + QUIT_COMMAND + "\" per terminare il programma"); try (Scanner in = new Scanner(System.in);) { String nomeFile; if (!(nomeFile = in.nextLine()).equals(QUIT_COMMAND)) { File file = new File(nomeFile); try (Scanner fileScanner = new Scanner(file)) { System.out.println("File trovato! \"" + nomeFile + "\", scrivi \"n\" per vedere le prossime 10 righe"); while (!(carattere = in.nextLine()).equals(QUIT_COMMAND)) { if (carattere.equals(NEXT_COMMAND)) { for (int i = 0; fileScanner.hasNext() && i < numeroRighe; i++) { System.out.println(fileScanner.nextLine()); } } } } catch (FileNotFoundException exc) { System.out.println("File non trovato!"); } } } catch (Exception ex) { ex.printStackTrace(); } System.out.println("Fine programma!"); } }
Segue un esempio di output:
java com.claudiodesio.io.More Digita il nome di un file e batti enter, oppure scrivi "q" per terminare il programma More.java File trovato! "More.java" n package com.claudiodesio.io; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class More { private final static int numeroRighe = 10; private static final String QUIT_COMMAND = "q"; n private static final String NEXT_COMMAND = "n"; public static void main(String args[]) { String carattere; System.out.println("Digita il nome di un file e batti enter, oppure " + "scrivi \"" + QUIT_COMMAND + "\" per terminare il programma"); try (Scanner in = new Scanner(System.in);) { String nomeFile; if (!(nomeFile = in.nextLine()).equals(QUIT_COMMAND)) { File file = new File(nomeFile); n try (Scanner fileScanner = new Scanner(file)) { System.out.println("File trovato! \"" + nomeFile + "\""); while (!(carattere = in.nextLine()).equals(QUIT_COMMAND)) { if (carattere.equals(NEXT_COMMAND)) { for (int i = 0; fileScanner.hasNext() && i < numeroRighe; i++) { System.out.println( fileScanner.nextLine()); } } } } catch (FileNotFoundException exc) { n System.out.println("File non trovato!"); } } } catch (Exception ex) { ex.printStackTrace(); } System.out.println("Fine programma!"); } } q Fine programma!
-
Esercizio 15.f)
Partendo dalla soluzione dell'esercizio 6.y, continuiamo a fare evolvere la nostra applicazione che simula il funzionamento di una rubrica. In questo e nei prossimi esercizi, realizzeremo quindi nuove classi della nostra applicazione passo dopo passo. Più in avanti andremo ad integrare le classi realizzate nell'esercizio 6.y con quelle che creeremo nei prossimi esercizi.
Utilizzeremo come "strato di persistenza dei dati", un componente che si occupa di serializzare e deserializzare oggetti sul file system. In altre parole vogliamo creare una classe che si chiamaGestoreFile
, appartenente al packagerubrica.integrazione
e che espone i seguenti metodi:
•inserisci
che prende in input un oggetto di tipoContatto
, e non ritorna nulla. Avrà il compito di serializzare sul nostro file system l'oggettoContatto
che prende in input, in un file con il nome del contatto (variabilenome
della classeContatto
) e con suffisso.con
. Per esempio se il nome del contatto è "Ross", allora il nome del file dovrà essereRoss.con
.
•recupera
che prende in input il nome del contatto da recuperare tramite deserializzazione. Per esempio se viene passato il nome "Phoebe Buffay", deve essere recuperato il filePhoebe Buffay.con
.L'unica classe dell'esercizio 6.y che per ora dobbiamo riusare è la classe
Contatto
, che però estendeva la classeEntita
, che a sua volta implementava le interfacceDato
eIdentificabile
, quindi partiamo da questi file. Al fine di far funzionare la serializzazione della classeContatto
, facciamo in modo che la classeEntita
vada ad implementare anche l'interfacciajava.io.Serializable
.
Inoltre sostituire il metodostampaDettagli
della classeContatto
con un metodotoString
.
Soluzione
Abbiamo deciso di definire definito una classe di utilità
FileUtils
nel seguente modo:
package rubrica.util; public class FileUtils { public static final String SUFFIX =".con"; public static String getNomeFile(String nome) { return nome + SUFFIX; } }
Poi abbiamo implementato la classeGestoreFile
, che si occupa di implementare i due metodi richiesti,inserisci
erecupera
, nel seguente modo:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.util.*; public class GestoreFile { public void inserisci(Contatto contatto) throws IOException { try (FileOutputStream fos = new FileOutputStream (FileUtils.getNomeFile(contatto.getNome())); ObjectOutputStream s = new ObjectOutputStream (fos);) { s.writeObject (contatto); } } public Contatto recupera(String nome) throws IOException, ClassNotFoundException { Contatto contatto = null; try (FileInputStream fis = new FileInputStream( FileUtils.getNomeFile(nome)); ObjectInputStream ois = new ObjectInputStream (fis);) { contatto = (Contatto)ois.readObject(); } return contatto; } }
Si noti che abbiamo utilizzato il costruttotry
-with-resources in entrambi i metodi. -
Esercizio 15.g)
Si crei una classe di test
Esercizio15G
con la quale si possa verificare che i metodi creati nella classeGestoreFile
nell'esercizio precedente funzionino correttamente. Questa classe deve creare un array di tre contatti, serializzarli e provare a rileggerli.
Soluzione
Segue la classe di test richiesta:
package rubrica.test; import rubrica.dati.*; import rubrica.integrazione.*; public class Esercizio15G { private GestoreFile gestoreFile; private Contatto[] contatti; Esercizio15G() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } private void creaContatto(Contatto contatto) { try { gestoreFile.inserisci(contatto); System.out.println("Contatto creato:\n"+ contatto); } catch (Exception exc) { exc.printStackTrace(); } } public void recuperaContatto(String nomeContatto) { try { Contatto contatto = gestoreFile.recupera(nomeContatto); System.out.println("Contatto recuperato:\n"+contatto); } catch (Exception exc) { exc.printStackTrace(); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1" ); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); Contatto[] contatti = { contatto1, contatto2, contatto3 } ; return contatti; } public static void main(String args[]) { Esercizio15G esercizio15G = new Esercizio15G(); esercizio15G.eseguiTest(); } }
che produrrà il seguente output:
TESTIAMO LA CREAZIONE DEI TRE CONTATTI Contatto creato: Nome: Daniele Telefono: 01234560 Indirizzo: Via delle chitarre 1 Contatto creato: Nome: Giovanni Telefono: 0565432190 Indirizzo: Via delle scienze 2 Contatto creato: Nome: Ligeia Telefono: 07899921 Indirizzo: Via dei segreti 3 RECUPERIAMO I TRE CONTATTI Contatto recuperato: Nome: Daniele Telefono: 01234560 Indirizzo: Via delle chitarre 1 Contatto recuperato: Nome: Giovanni Telefono: 0565432190 Indirizzo : Via delle scienze 2 Contatto recuperato: Nome: Ligeia Telefono: 07899921 Indirizzo: Via dei segreti 3
-
Esercizio 15.h)
Partendo dalla soluzione dell'esercizio precedente, creare la classe
Esercizio15H
a partire dalla classeEsercizio15G
aggiungendo (e chiamando) due metodi di test che testano:
Quali tra le seguenti affermazioni è corretta?
• cosa succede se si prova ad aggiungere un contatto già esistente;Riflettete sui risultati dei test e:
• cosa succede se si prova a recuperare un contatto inesistente.• progettare le soluzioni degli eventuali problemi che sono stati riscontrati
• implementare le soluzioni progettate
Soluzione
Potremmo codificare i nuovi metodi come nel seguente file:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; public class Esercizio15H { private GestoreFile gestoreFile; private Contatto[] contatti; Esercizio15H() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } private void creaContatto(Contatto contatto) { try { gestoreFile.inserisci(contatto); System.out.println("Contatto creato:\n"+ contatto); } catch (Exception exc) { exc.printStackTrace(); } } public void recuperaContatto(String nomeContatto) { try { Contatto contatto = gestoreFile.recupera(nomeContatto); System.out.println("Contatto recuperato:\n"+contatto); } catch (Exception exc) { exc.printStackTrace(); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1" ); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); Contatto[] contatti = { contatto1, contatto2, contatto3 } ; return contatti; } public static void main(String args[]) { Esercizio15H esercizio15H = new Esercizio15H(); esercizio15H.eseguiTest(); } }
L'output risultante sarà:
TESTIAMO LA CREAZIONE DEI TRE CONTATTI Contatto creato: Nome: Daniele Telefono: 01234560 Indirizzo: Via delle chitarre 1 Contatto creato: Nome: Giovanni Telefono: 0565432190 Indirizzo: Via delle scienze 2 Contatto creato: Nome: Ligeia Telefono: 07899921 Indirizzo: Via dei segreti 3 RECUPERIAMO I TRE CONTATTI Contatto recuperato: Nome: Daniele Telefono: 01234560 Indirizzo: Via delle chitarre 1 Contatto recuperato: Nome: Giovanni Telefono: 0565432190 Indirizzo: Via delle scienze 2 Contatto recuperato: Nome: Ligeia Telefono: 07899921 Indirizzo: Via dei segreti 3 TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE Contatto creato: Nome: Daniele Telefono: 01234560 Indirizzo: Via delle chitarre 1 PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE java.io.FileNotFoundException: Pippo.con (Impossibile trovare il file specificato) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:213) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110) at rubrica.integrazione.GestoreFile.recupera(GestoreFile.java:51) at rubrica.test.Esercizio15H.recuperaContattoNonEsistente(Esercizio15H.java:37) at rubrica.test.Esercizio15H.eseguiTest(Esercizio15H.java:23) at rubrica.test.Esercizio15H.main(Esercizio15H.java:88)
Si noti che il test ha dimostrato che:
• aggiungere un contatto esistente provoca la sovrascrittura del vecchio contatto;Entrambi i risultati dei test non ci soddisfano. Nel primo caso, non ci sembra corretto che il vecchio contatto sia sostituito senza neanche che l'utente ne venga a conoscenza. Decidiamo per l'implementazione di una semplice soluzione che prevede di controllare se il contatto esiste già, prima di serializzarlo, e nel caso lanciare un'eccezione personalizzata che potrebbe chiamarsi
• recuperare un contatto inesistente provoca un'eccezione di tipoFileNotFoundException
.ContattoEsistenteException
.
Nel secondo caso sarebbe preferibile creare un'eccezione personalizzata che potremmo chiamareContattoInesistenteException
, contenente un messaggio leggibile.
Implementiamo ora la soluzione.
Per prima cosa definiamo le eccezioni in maniera semplicistica, segue l'eccezioneContattoInesistenteException
:
package rubrica.eccezioni; import java.io.IOException; public class ContattoInesistenteException extends IOException { private static final long serialVersionUID = 8942402240056525663L; public ContattoInesistenteException (String message){ super(message); } }
E l'altra eccezioneContattoEsistenteException
:
package rubrica.eccezioni; import java.io.IOException; public class ContattoEsistenteException extends IOException { private static final long serialVersionUID = 8942402240056525662L; public ContattoEsistenteException (String message){ super(message); } }
Notare che abbiamo fatto estendere ad entrambe le classi l'eccezioneIOException
. Modifichiamo la classeGestoreFile
nel seguente modo:
import java.util.*; import java.io.*; package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; public class GestoreFile { public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Contatto contattoEsistente = getContatto( FileUtils.getFileName(contatto.getNome())); if (contattoEsistente != null) { throw new ContattoEsistenteException(contatto.getNome() +": contatto già esistente!"); } try (FileOutputStream fos = new FileOutputStream ( new File(FileUtils.getNomeFile(contatto.getNome()))); ObjectOutputStream s = new ObjectOutputStream (fos);) { s.writeObject (contatto); } } public Contatto recupera(String nome) throws ContattoInesistenteException { Contatto contatto = getContatto(FileUtils.getFileName(nome)); if (contatto == null) { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } return contatto; } private Contatto getContatto(String nome) { try (FileInputStream fis = new FileInputStream (new File(nome)); ObjectInputStream ois = new ObjectInputStream (fis);) { Contatto contatto = (Contatto)ois.readObject(); return contatto; } catch (Exception exc) { return null; } } }
Si noti che abbiamo aggiunto il metodo privatogetContatto
, che ritorna il contatto se esiste, oppurenull
se non viene trovato. Tale metodo viene utilizzato sia dal metodoinserisci
che dal metodorecupera
, al fine di gestire le eventuali eccezioni. Infine modifichiamo anche la classe di test sostituendo, con la stampa del metodogetMessage
delle eccezioni, lo statement che stampava lo stack trace dell'eccezione:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; public class Esercizio15H { private GestoreFile gestoreFile; private Contatto[] contatti; Esercizio15H() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } private void creaContatto(Contatto contatto) { try { gestoreFile.inserisci(contatto); System.out.println("Contatto creato:\n"+ contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void recuperaContatto(String nomeContatto) { try { Contatto contatto = gestoreFile.recupera(nomeContatto); System.out.println("Contatto recuperato:\n" + contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1" ); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); Contatto[] contatti = { contatto1, contatto2, contatto3 } ; return contatti; } public static void main(String args[]) { Esercizio15H esercizio15H = new Esercizio15H(); esercizio15H.eseguiTest(); } }
Cancellare i file.con
generati dalla precedente esecuzione per non ottenere i risultati falsati nel test.
L'output di questa classe adesso risulterà essere:
TESTIAMO LA CREAZIONE DEI TRE CONTATTI Contatto creato: Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto creato: Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto creato: Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 RECUPERIAMO I TRE CONTATTI Contatto recuperato: Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto recuperato: Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto recuperato: Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE Daniele: contatto già esistente! PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE Pippo: contatto non trovato!
-
Esercizio 15.i)
Partendo dalla soluzione dell'esercizio precedente, aggiungiamo nella classe
GestioneFile
i metodi:
•modifica
che modifica un contatto (non si dovrà poter cambiare il nome del contatto);
•rimuovi
che cancella il contatto con il nome specificato.Creare anche i metodi nella classe di test che testano il corretto funzionamento dei metodi
modifica
erimuovi
.
Soluzione
Coerentemente con la linea di sviluppo tenuta sinora, il file
GestoreFile
, potrebbe evolversi nel seguente modo:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; public class GestoreFile { public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Contatto contattoEsistente = getContatto( FileUtils.getFileName(contatto.getNome())); if (contattoEsistente != null) { throw new ContattoEsistenteException( contatto.getNome() +": contatto già esistente!"); } registra(contatto); } public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { Contatto contatto = getContatto(FileUtils.getFileName(nome)); if (contatto == null) { throw new ContattoInesistenteException( nome + ": contatto non trovato!"); } return contatto; } public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { if (isContattoEsistente(contatto.getNome())) { registra(contatto); } else { throw new ContattoInesistenteException(contatto.getNome() + ": contatto non trovato!"); } } public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { File file = new File(FileUtils.getNomeFile(nome)); if (!file.delete()) { throw new ContattoInesistenteException( nome + ": contatto non trovato!"); } } private void registra(Contatto contatto) throws FileNotFoundException, IOException { try (FileOutputStream fos = new FileOutputStream ( new File(FileUtils.getNomeFile(contatto.getNome()))); ObjectOutputStream s = new ObjectOutputStream (fos);) { s.writeObject (contatto); } } private boolean isContattoEsistente(String nome) { File file = new File(FileUtils.getNomeFile(nome)); return file.exists(); } private Contatto getContatto(String nome) { try (FileInputStream fis = new FileInputStream (new File()); ObjectInputStream ois = new ObjectInputStream (fis);) { Contatto contatto = (Contatto)ois.readObject(); return contatto; } catch (Exception exc) { return null; } } }
Si noti che il metodo privatoisContattoEsistente
controlla solo se un file con il nome del contatto esiste, e questo nel nostro caso dovrebbe essere sufficiente. Invochiamo questo metodo dal metodomodifica
prima di registrare (e sovrascrivere) il vecchio contatto. Nel caso il file non esistesse, otterremmo il lancio di un'eccezioneContattoInesistenteException
. Il metodorimuovi
invece, sfrutta il metododelete
della classeFile
. Questo ritornatrue
se e solo se è riuscito a cancellare il file. La classe di test potremmo modificarla nel seguente modo, per andare a testare gli scenari della modifica e della cancellazione:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; public class Esercizio15I { private GestoreFile gestoreFile; private Contatto[] contatti; Esercizio15I() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO ESISTENTE"); modificaContattoEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO ESISTENTE"); rimuoviContattoEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO NON ESISTENTE"); modificaContattoNonEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO NON ESISTENTE"); rimuoviContattoNonEsistente(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void modificaContattoEsistente() { try { Contatto contatto = new Contatto("Daniele", "Via dei microfoni 1","07890"); gestoreFile.modifica(contatto); System.out.println("Contatto "+ contatto.getNome() + " modificato!\n" + contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void rimuoviContattoEsistente() { try { gestoreFile.rimuovi(contatti[2].getNome()); System.out.println("Contatto "+ contatti[2].getNome() + " cancellato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void modificaContattoNonEsistente() { try { Contatto contatto = new Contatto("Pluto", "Via dei microfoni 1","07890"); gestoreFile.modifica(contatto); System.out.println("Contatto "+ contatto.getNome() +" modificato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void rimuoviContattoNonEsistente() { try { String nome ="Ligeia"; gestoreFile.rimuovi(nome); System.out.println("Contatto "+ nome +" cancellato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void recuperaContatto(String nomeContatto) { try { gestoreFile.recupera(nomeContatto); System.out.println("Contatto "+ nomeContatto +" recuperato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private void creaContatto(Contatto contatto) { try { gestoreFile.inserisci(contatto); System.out.println("Contatto creato:\n"+ contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1" ); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); Contatto[] contatti = {contatto1, contatto2, contatto3}; return contatti; } public static void main(String args[]) { Esercizio15I Esercizio15I = new Esercizio15I(); Esercizio15I.eseguiTest(); } }
L'output risultante sarà il seguente:
TESTIAMO LA CREAZIONE DEI TRE CONTATTI Contatto Daniele creato! Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto Giovanni creato! Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto Ligeia creato! Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 RECUPERIAMO I TRE CONTATTI Contatto recuperato! Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto recuperato! Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto recuperato! Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE Daniele: contatto già esistente! PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE Pippo: contatto non trovato! MODIFICHIAMO UN CONTATTO ESISTENTE Contatto Daniele modificato! RIMUOVIAMO UN CONTATTO ESISTENTE Contatto Ligeia cancellato! MODIFICHIAMO UN CONTATTO NON ESISTENTE Pluto: contatto non trovato! RIMUOVIAMO UN CONTATTO NON ESISTENTE Ligeia: contatto non trovato!
-
Esercizio 15.j)
Partendo dalla soluzione dell'esercizio precedente aggiungiamo un altro requisito, questa volta non funzionale ma architetturale: applicando il principio di inversione della dipendenza (DIP, introdotto nel paragrafo H.2.1 dell'appendice H). Come già fatto in precedenza per la classe
Contatto
, facciamo quindi evolvere la classeGestoreFile
, in modo tale che rappresenti un'implementazione di uno strumento generico per interagire con un meccanismo di immagazzinamento qualsiasi.
Creiamo quindi un'interfacciaGestoreSerializzazione
da far implementare aGestoreFile
. Questa modifica ci permetterà di cambiare facilmente il modo in cui serializziamo i nostri oggetti (vedi esercizio 15.l).
Soluzione
Potremmo creare la seguente classe astratta
GestoreSerializzazione
:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; public interface GestoreSerializzazione<T extends Dato> { void inserisci(T dato) throws IOException; T recupera(String id) throws IOException, ClassNotFoundException; void modifica(T dato) throws IOException; void rimuovi(String id) throws IOException; }
per farla implementare aGestoreFile
:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; public class GestoreFile implements GestoreSerializzazione<Contatto> { @Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Contatto contattoEsistente = getContatto( FileUtils.getFileName(contatto.getNome())); if (contattoEsistente != null) { throw new ContattoEsistenteException( contatto.getNome() +": contatto già esistente!"); } registra( contatto); } @Override public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { Contatto contatto = getContatto(FileUtils.getFileName(nome)); if (contatto == null) { throw new ContattoInesistenteException( nome + ": contatto non trovato!"); } return contatto; } @Override public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { if (isContattoEsistente(contatto.getNome())) { registra(contatto); } else { throw new ContattoInesistenteException( contatto.getNome() +": contatto non trovato!"); } } @Override public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { File file = new File(FileUtils.getNomeFile(nome)); if (!file.delete()){ throw new ContattoInesistenteException( nome + ": contatto non trovato!"); } } private void registra(Contatto contatto) throws FileNotFoundException, IOException { try (FileOutputStream fos = new FileOutputStream ( new File(FileUtils.getNomeFile(contatto.getNome()))); ObjectOutputStream s = new ObjectOutputStream (fos);) { s.writeObject (contatto); } } private boolean isContattoEsistente(String nome) { File file = new File(FileUtils.getNomeFile(nome)); return file.exists(); } private Contatto getContatto(String nome) { try (FileInputStream fis = new FileInputStream(new File(nome)); ObjectInputStream ois = new ObjectInputStream (fis);) { Contatto contatto = (Contatto)ois.readObject(); return contatto; } catch (Exception exc) { return null; } } }
Infine ecco la classe di test:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; public class Esercizio15J { private GestoreSerializzazione<Contatto> gestoreFile; private Contatto[] contatti; Esercizio15J() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO ESISTENTE"); modificaContattoEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO ESISTENTE"); rimuoviContattoEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO NON ESISTENTE"); modificaContattoNonEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO NON ESISTENTE"); rimuoviContattoNonEsistente(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void modificaContattoEsistente() { try { Contatto contatto = new Contatto("Daniele", "Via dei microfoni 1","07890"); gestoreFile.modifica(contatto); System.out.println("Contatto "+ contatto.getNome() + " modificato!\n" + contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void rimuoviContattoEsistente() { try { gestoreFile.rimuovi(contatti[2].getNome()); System.out.println("Contatto "+ contatti[2].getNome() + " cancellato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void modificaContattoNonEsistente() { try { Contatto contatto = new Contatto("Pluto", "Via dei microfoni 1","07890"); gestoreFile.modifica(contatto); System.out.println("Contatto "+ contatto.getNome() +" modificato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void rimuoviContattoNonEsistente() { try { String nome ="Ligeia"; gestoreFile.rimuovi(nome); System.out.println("Contatto "+ nome +" cancellato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void recuperaContatto(String nomeContatto) { try { gestoreFile.recupera(nomeContatto); System.out.println("Contatto "+ nomeContatto +" recuperato!"); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private void creaContatto(Contatto contatto) { try { gestoreFile.inserisci(contatto); System.out.println("Contatto creato:\n"+ contatto); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1" ); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); Contatto[] contatti = {contatto1, contatto2, contatto3}; return contatti; } public static void main(String args[]) { Esercizio15J esercizio15J = new Esercizio15J(); esercizio15J.eseguiTest(); } }
-
Esercizio 15.k)
Partendo dalla soluzione dell'esercizio precedente aggiungiamo un altro requisito. Facciamo un po' di refactoring del codice, eliminando il codice duplicato nelle classi create. In particolare creare la classe
Esercizio15k
, eliminando le duplicazioni di codice che erano presenti nella classeEsercizion15j
.
Naturalmente il lettore è libero di aggiungere qualsiasi miglioramento al proprio codice in qualsiasi classe.
Nello sviluppo è molto importante ad un certo punto fermarsi a riflettere, e migliorare il proprio codice, tramite "refactoring", ovvero cambiare la struttura interna del software, senza modificarne il comportamento esterno. Questo a qualcuno può sembrare una perdita di tempo ma in realtà è esattamente il contrario. Il refactoring migliora la qualità del codice a livello non funzionale migliorandone le caratteristiche architetturali. Favorisce alcune di quelle che vengono dette "qualità sistemiche del software", per esempio promuove la manutenibilità e l'estensibilità del codice, riducendone la complessità. In questo modo avremo meno bachi e più consapevolezza del nostro codice. Il mio consiglio personale è quello di dedicare una percentuale giornaliera del tempo di sviluppo che varia dal 10% al 25% al refactoring, possibilmente nel momento in cui ci si rende conto di essere troppo stanchi. Per esempio in una giornata lavorativa da 8 ore, le ultime due (o almeno l'ultima) potrebbe essere dedicata a migliorare la qualità del software. Per informazioni sul refactoring potete:
- andare sul sito https://refactoring.com;
- comprare il libro "Refactoring" di Martin Fowler, come consigliato nella bibliografia online;
- utilizzare i meccanismi di refactoring che offrono gli IDE più famosi.
Soluzione
Una soluzione potrebbe essere quella di usare delle espressioni lambda per evitare di riscrivere i vari metodi gestendo sempre allo stesso modo le eccezioni. In particolare possiamo creare due tipologie di interfacce funzionali. Una che definisce un metodo che restituisce
void
:
package rubrica.util; @FunctionalInterface public interface Executor { void esegui() throws Exception; }
Ed un'altra che restituisce un tipo generico:
package rubrica.util; @FunctionalInterface public interface Retriever<O> { O esegui() throws Exception; }
Non abbiamo utilizzato un'interfaccia funzionalejava.util.function.Consumer
al posto diExecutor
, e l'interfaccia funzionalejava.util.function.Supplier
al posto diRetriever
, perché ci occorreva che i metodi SAM dichiarassero la clausolathrows
adException
.
Possiamo sfruttare queste interfacce funzionali nella classeEsercizio15K
nel seguente modo:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import rubrica.util.*; public class Esercizio15K { private GestoreSerializzazione<Contatto> gestoreFile; private Contatto[] contatti; Esercizio15K() { contatti = getContatti(); gestoreFile = new GestoreFile(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO ESISTENTE"); modificaContattoEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO ESISTENTE"); rimuoviContattoEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO NON ESISTENTE"); modificaContattoNonEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO NON ESISTENTE"); rimuoviContattoNonEsistente(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void modificaContattoEsistente() { Contatto contatto = new Contatto("Daniele","Via dei microfoni 1", "07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!\n"+ contatto; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoEsistente() { String nome = contatti[2].getNome(); String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void modificaContattoNonEsistente() { Contatto contatto = new Contatto("Pluto","Via dei microfoni 1","07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!"; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoNonEsistente() { String nome ="Ligeia"; String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void recuperaContatto(String nomeContatto) { esegui(()->gestoreFile.recupera(nomeContatto)); } private void creaContatto(Contatto contatto) { String messaggio ="Contatto "+ contatto.getNome() +" creato!\n"+ contatto; esegui(()->gestoreFile.inserisci(contatto), messaggio); } public <O> O esegui(Retriever<O> retriever) { O output = null; try { output = retriever.esegui(); System.out.println("Contatto recuperato!\n"+ output); } catch (Exception exc) { System.out.println(exc.getMessage()); } return output; } public void esegui(Executor executor, String messaggio) { try { executor.esegui(); System.out.println(messaggio); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele","Via delle chitarre 1","01234560"); Contatto contatto2 = new Contatto("Giovanni","Via delle scienze 2","0565432190"); Contatto contatto3 = new Contatto("Ligeia","Via dei segreti 3","07899921"); Contatto[] contatti = {contatto1, contatto2, contatto3}; return contatti; } public static void main(String args[]) { Esercizio15K esercizio15K = new Esercizio15K(); esercizio15K.eseguiTest(); } }
Si noti che abbiamo dovuto creare i due metodiesegui
di cui uno prende in input unRetriever
e l'altro unExecutor
. Questi metodi inseriscono la chiamata al metodoesegui
diRetriever
oExecutor
all'interno della gestione delle eccezioni che prima veniva replicata in ogni metodo. In questo modo, passando un'espressione lambda contenente il codice da eseguire a questi due nuovi metodi, abbiamo potuto evitare le duplicazioni presenti nella classeEsercizio15J
.
Segue l'output:
TESTIAMO LA CREAZIONE DEI TRE CONTATTI Contatto Daniele creato! Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto Giovanni creato! Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto Ligeia creato! Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 RECUPERIAMO I TRE CONTATTI Contatto recuperato! Nome: Daniele Indirizzo: Via delle chitarre 1 Telefono: 01234560 Contatto recuperato! Nome: Giovanni Indirizzo: Via delle scienze 2 Telefono: 0565432190 Contatto recuperato! Nome: Ligeia Indirizzo: Via dei segreti 3 Telefono: 07899921 TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE Daniele: contatto già esistente! PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE Pippo: contatto non trovato! MODIFICHIAMO UN CONTATTO ESISTENTE Contatto Daniele modificato! Nome: Daniele Indirizzo: 07890 Telefono: Via dei microfoni 1 RIMUOVIAMO UN CONTATTO ESISTENTE Contatto Ligeia cancellato! MODIFICHIAMO UN CONTATTO NON ESISTENTE Pluto: contatto non trovato! RIMUOVIAMO UN CONTATTO NON ESISTENTE Ligeia: contatto non trovato!
-
Esercizio 15.l)
Partendo dalla soluzione dell'esercizio precedente, creiamo anche un'implementazione alternativa della classe
GestoreFile
, che fa uso della libreria NIO2 (classeGestoreFileNIO2
). Implementare anche la classe di testEsercizio15L
.
Soluzione
La soluzione potrebbe essere la seguente:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; import java.nio.file.*; public class GestoreFileNIO2 implements GestoreSerializzazione<Contatto> { @Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(contatto.getNome())); if (Files.exists(path)) { throw new ContattoEsistenteException( contatto.getNome() + ": contatto già esistente!"); } registra(contatto); } @Override public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { Contatto contatto = getContatto(FileUtils.getFileName(nome)); if (contatto == null) { throw new ContattoInesistenteException( nome + ": contatto non trovato!"); } return contatto; } @Override public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { if (isContattoEsistente(contatto.getNome())) { registra(contatto); } else { throw new ContattoInesistenteException(contatto.getNome() + ": contatto non trovato!"); } } @Override public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(nome)); if (Files.exists(path)) { Files.delete(path); } else { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } } private void registra(Contatto contatto) throws FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(contatto.getNome())); Files.write(path, getBytesDaOggetto(contatto)); } private byte[] getBytesDaOggetto(Object object) throws IOException { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) { out.writeObject(object); return bos.toByteArray(); } } private Object getOggettoDaByte(byte[] bytes) throws IOException, ClassNotFoundException { try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) { return in.readObject(); } } private boolean isContattoEsistente(String nome) { Path path = Paths.get(FileUtils.getNomeFile(nome)); return Files.exists(path); } private Contatto getContatto(String nome) { Path path = Paths.get(nome); byte[] bytes = null; Contatto contatto = null; try { bytes = Files.readAllBytes(path); contatto = (Contatto)getOggettoDaByte(bytes); } catch (Exception exc) { return null; } return contatto; } }
La classe per testareEsercizio15N
, si differenzia dalla classeEsercizio15M
solo dall'applicazione del principio DIP che istanzia la nuova classeGestorFileNIO2
, usando un reference di tipoGestoreSerializzazione
. Tutto il resto non cambia.
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import rubrica.util.*; public class Esercizio15L { private GestoreSerializzazione<Contatto> gestoreFile; private Contatto[] contatti; Esercizio15L() { contatti = getContatti(); gestoreFile = new GestoreFileNIO2(); } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO ESISTENTE"); modificaContattoEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO ESISTENTE"); rimuoviContattoEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO NON ESISTENTE"); modificaContattoNonEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO NON ESISTENTE"); rimuoviContattoNonEsistente(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void modificaContattoEsistente() { Contatto contatto = new Contatto("Daniele","Via dei microfoni 1", "07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!\n"+ contatto; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoEsistente() { String nome = contatti[2].getNome(); String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void modificaContattoNonEsistente() { Contatto contatto = new Contatto("Pluto","Via dei microfoni 1","07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!"; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoNonEsistente() { String nome ="Ligeia"; String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void recuperaContatto(String nomeContatto) { esegui(()->gestoreFile.recupera(nomeContatto)); } private void creaContatto(Contatto contatto) { String messaggio ="Contatto "+ contatto.getNome() +" creato!\n"+ contatto; esegui(()->gestoreFile.inserisci(contatto), messaggio); } public <O> O esegui(Retriever<O> retriever) { O output = null; try { output = retriever.esegui(); System.out.println("Contatto recuperato!\n"+ output); } catch (Exception exc) { System.out.println(exc.getMessage()); } return output; } public void esegui(Executor executor, String messaggio) { try { executor.esegui(); System.out.println(messaggio); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele","Via delle chitarre 1","01234560"); Contatto contatto2 = new Contatto("Giovanni","Via delle scienze 2","0565432190"); Contatto contatto3 = new Contatto("Ligeia","Via dei segreti 3","07899921"); Contatto[] contatti = {contatto1, contatto2, contatto3}; return contatti; } public static void main(String args[]) { Esercizio15L esercizio15L = new Esercizio15L(); esercizio15L.eseguiTest(); } }
Possiamo ritornare alla vecchia versione semplicemente sostituendo all'istanza diGestioneFileNIO2
l'istanza diGestioneFile
. -
Esercizio 15.m)
Partendo dall'esercizio precedente, usando un oggetto di tipo
Properties
(introdotto nel appendice G), creare un file di configurazioneconfig.properties
con contiene un'unica property, che servirà alla nostra applicazione per scegliere se utilizzare per la serializzazione, la classeGestoreFile
o la classeGestoreFileNIO2
. Creare una classeGestoreSerializzazioneFactory
, che implementa il pattern Factory Method definendo il metodogetGestoreSerializzazione
. Infine creare la classeEsercizio15M
a partire dalla classeEsercizio15L
, che sfrutta il meccanismo appena descritto per la serializzazione degli oggetti.
Soluzione
Abbiamo implementato la classe
GestoreSerializzazioneFactory
, sfruttando anche la reflection. Tuttavia anche una soluzione che faceva uso di una espressioneswitch
o qualsiasi costrutto condizionale sarebbe stata valida:
package rubrica.integrazione; import rubrica.dati.Contatto; import java.util.Properties; import java.io.*; public class GestoreSerializzazioneFactory { private static final String PACKAGE ="rubrica.integrazione."; private static final String SERIALIZATION_METHOD ="rubrica.ser"; private static Properties properties; static { properties = new Properties(); try { loadProperties(); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public static GestoreSerializzazione<Contatto> getGestoreSerializzazione() throws InstantiationException, ClassNotFoundException, IllegalAccessException { String className = properties.getProperty(SERIALIZATION_METHOD); System.out.println("Sto caricando la classe " + className); Class<?> classObject = Class.forName(PACKAGE + className); return (GestoreSerializzazione<Contatto>)classObject.newInstance(); } private static void loadProperties() throws IOException { try (FileInputStream inputStream = new FileInputStream("config.properties");) { properties.load(inputStream); } } }
Ovviamente abbiamo supposto che il valore della property coincida con il nome della classe da caricare. Per esempio potremmo salvare il fileconfig.properties
con il seguente contenuto:
rubrica.ser=GestoreFile
Infine possiamo testare la nostra implementazione con la seguente classeEsercizio15M
:
package rubrica.test; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import rubrica.util.*; public class Esercizio15M { private GestoreSerializzazione<Contatto> gestoreFile; private Contatto[] contatti; Esercizio15M() { contatti = getContatti(); try { gestoreFile = GestoreSerializzazioneFactory.getGestoreSerializzazione(); } catch (Exception exc) { exc.printStackTrace(); System.exit(1); } } private void eseguiTest() { System.out.println("TESTIAMO LA CREAZIONE DEI TRE CONTATTI"); creaContatti(); System.out.println("RECUPERIAMO I TRE CONTATTI"); recuperaContatti(); System.out.println( "TESTIAMO LA CREAZIONE DI UN CONTATTO GIÀ ESISTENTE"); creaContattoEsistente(); System.out.println("PROVIAMO A RECUPERARE UN CONTATTO NON ESISTENTE"); recuperaContattoNonEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO ESISTENTE"); modificaContattoEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO ESISTENTE"); rimuoviContattoEsistente(); System.out.println("MODIFICHIAMO UN CONTATTO NON ESISTENTE"); modificaContattoNonEsistente(); System.out.println("RIMUOVIAMO UN CONTATTO NON ESISTENTE"); rimuoviContattoNonEsistente(); } public void creaContatti() { for (Contatto contatto : contatti) { creaContatto(contatto); } } public void recuperaContatti() { for (Contatto contatto : contatti) { recuperaContatto(contatto.getNome()); } } public void creaContattoEsistente() { creaContatto(contatti[0]); } public void modificaContattoEsistente() { Contatto contatto = new Contatto("Daniele","Via dei microfoni 1", "07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!\n"+ contatto; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoEsistente() { String nome = contatti[2].getNome(); String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void modificaContattoNonEsistente() { Contatto contatto = new Contatto("Pluto","Via dei microfoni 1","07890"); String messaggio ="Contatto "+ contatto.getNome() +" modificato!"; esegui(()->gestoreFile.modifica(contatto), messaggio); } public void rimuoviContattoNonEsistente() { String nome ="Ligeia"; String messaggio ="Contatto "+ nome +" cancellato!"; esegui(()->gestoreFile.rimuovi(nome), messaggio); } public void recuperaContattoNonEsistente() { recuperaContatto("Pippo"); } public void recuperaContatto(String nomeContatto) { esegui(()->gestoreFile.recupera(nomeContatto)); } private void creaContatto(Contatto contatto) { String messaggio ="Contatto "+ contatto.getNome() +" creato!\n"+ contatto; esegui(()->gestoreFile.inserisci(contatto), messaggio); } public <O> O esegui(Retriever<O> retriever) { O output = null; try { output = retriever.esegui(); System.out.println("Contatto recuperato!\n"+ output); } catch (Exception exc) { System.out.println(exc.getMessage()); } return output; } public void esegui(Executor executor, String messaggio) { try { executor.esegui(); System.out.println(messaggio); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private Contatto[] getContatti() { Contatto contatto1 = new Contatto("Daniele","Via delle chitarre 1","01234560"); Contatto contatto2 = new Contatto("Giovanni","Via delle scienze 2","0565432190"); Contatto contatto3 = new Contatto("Ligeia","Via dei segreti 3","07899921"); Contatto[] contatti = {contatto1, contatto2, contatto3}; return contatti; } public static void main(String args[]) { Esercizio15M esercizio15M = new Esercizio15M(); esercizio15M.eseguiTest(); } }
-
Esercizio 15.n)
Questo e i prossimi esercizi, sono da considerarsi particolarmente difficili. Sono esercizi difficili anche per un professionista. A volte bisognerà andare su internet per studiare librerie e soluzioni, e cercare di essere molto lucidi. Quindi anche se i risultati non saranno brillanti, non dovete preoccuparvi. Se vi bloccate, leggete le soluzioni e andate avanti. Se invece riuscite a risolverli in qualche modo, allora significa che siete arrivati ad un livello di programmazione davvero importante.
A partire da questo esercizio, creeremo una interfaccia utente da riga di comando. È un esercizio complesso che richiede un procedimento passo dopo passo. In questo primo esercizio, usando un oggetto di tipo
Scanner
, definiremo quindi una classeRubricaCLI
, che rappresenta una interfaccia utente da riga di comando ("CLI" è l'acronimo di "Command Line Interface", ovvero "interfaccia da riga di comando"). Useremo il pattern architetturale Model View Controller (MVC) che viene schematizzato nella Figura 15.n.1.
Figura 15.n.1 - Model View Controller.
Notare che la freccia che va dal Model alla View è stata disegnata tratteggiata, perché anche se rappresenta una responsabilità del classico pattern MVC, non sarà implementata nel nostro caso (come spesso accade), per rendere indipendente il componente Model dal componente View. I dati del Model aggiornati, saranno passati alla View direttamente dal Controller.
Il pattern MVC promuove la separazione delle responsabilità in un software in componenti diversi, definendo tre ruoli principali, per l'appunto il Model, la View e il Controller. Questa distinzione ci garantirà una serie di vantaggi dal punto di vista della manutenibilità, la facilità di trovare bug, la riusabilità, e così via. Daremo per scontato che il ruolo di Model sarà interpretato dall'insieme delle classi che abbiamo creato (a parte le classi di test) e che sinora compongono la nostra applicazione. La classe invece,
RubricaCLI
rappresenterà il componente View, ovvero il componente che implementa la cosiddetta logica di presentazione. Questo significa che dovrà avere due responsabilità fondamentali:• definire le "schermate" per l'utente (che nel nostro caso consisteranno in del semplice testo)
• notificare al componente controller eventuali input dell'utente (implementazione che faremo a partire dal prossimo esercizio).Inoltre nel nostro caso, vogliamo sottolineare che la nostra classe
RubricaCLI
, avrà una responsabilità aggiuntiva:• gestire le eccezioni (ovvero deve utilizzare blocchitry-catch
per visualizzare eventuali eccezioni che si sono propagate dal componente Model).Questo significa che i metodi dei componenti Controller e Model non gestiranno eccezioni ma semplicemente ne permetteranno la propagazione. In altre parole, i metodi dei componenti Controller e Model non utilizzeranno blocchi
try-catch
, ma clausolethrows
, per catturare eccezioni lanciate con la parola chiavethrow
. La nostra implementazione deve seguire le specifiche rappresentate nel diagramma degli stati rappresentato in figura 15.n.2.
Figura 15.n.2 - Diagramma degli stati.
Il diagramma degli stati solitamente viene utilizzato per descrivere gli stati di un'entità (per esempio di un oggetto), che assume durante il proprio ciclo di vita. Esso definisce come elementi principali gli stati (rappresentati come rettangoli con spigoli arrotondati) e le transizioni tra gli stati. Queste ultime hanno un verso, e possono essere etichettate con una sintassi costituita da tre parti (tutte opzionali):Il diagramma è piuttosto complesso, ma basterà fare un passo alla volta per poterlo interpretare, e alla fine scopriremo che è una specifica fondamentale per il nostro programma. La programmazione di interfacce utente infatti, differisce molto dalla programmazione tradizionale che abbiamo utilizzato sinora. Un diagramma come quello specificato nella figura 15.n.2 ci permetterà di non dover improvvisare una soluzione.evento-scatenante[condizione]/azione
. Inoltre ci sono gli elementi di inizio e fine che abbiamo già incontrato con il diagramma delle attività. Altre informazioni sulla sintassi UML si trovano nell'approfondimento 4.7.
Nel diagramma abbiamo utilizzato gli stati per rappresentare le "schermate testuali" che devono essere visualizzate, e le transizioni etichettate con gli eventi scatenanti che portano da una schermata ad un'altra.Notare che abbiamo specificato un protocollo ben preciso, dove per ogni schermata esiste un particolare insieme di comandi da poter specificare, per passare ad un'altra schermata. Tutti i comandi operativi sono costituiti da una sola lettera preceduto dal simbolo "/
",Per realizzare la classe
RubricaCLI
, iniziamo quindi a creare i metodi rappresentati negli stati del diagramma. Questi dovranno:• stampare dei messaggi coerentiIn questo esercizio quindi, il nostro compito si deve limitare a:
• catturare l'input dell'utente con un oggetto Scanner (dichiararlo come variabile d'istanza).
• creare la classeRubricaCLI
, che abbia come variabile d'istanza un oggettoScanner
inizializzata in un apposito costruttore.
• Definire il metodovisualizzaContatti
che prende in input un oggettoList
di contatti e ritornivoid
. Tale metodo dovrà per ora implementare il flusso descritto nel diagramma, ovvero, dopo aver stampato la lista dei contatti che prende in input, e la lista dei comandi possibili da specificare, aspettare l'input dell'utente e con dei costrutti condizionali stampare solo (per ora) la descrizione del comando specificato dell'utente, poi il programma può terminare. Per esempio se l'utente specifica il comando "/i
", allora il programma deve stampare "Chiamato metodo inserisci contatto". Se invece l'utente specifica il comando "/v
" seguito dal nome di un contatto presente nella lista (supponiamo il nome "Giovanni"), il programma deve stampare "Chiamato metodo visualizza contatto per Giovanni". Infine se l'utente specifica il comando "/t
" il programma deve terminare.
• Creare un'eccezione chiamataComandoNonValidoException
che deve essere lanciata nel caso in cui il comando specificato dall'utente non sia valido.
• Lanciare l'eccezioneContattoInesistenteException
(già creata in precedenza) nel caso l'utente inserisca il comando per visualizzare il contatto "/v
" seguito da un nome che non si trova nella lista.
• Infine creare la classeEsercizion15N
che servirà per lanciare l'applicazione (bisogna istanziare l'oggettoRubricaCLI
e chiamare il metodovisualizzaContatti
passandogli in input una lista di oggettiContatto
).
Nei prossimi esercizi faremo evolvere questa classe, fino a farla diventare pienamente funzionante.
A partire da questo esercizio, per catturare l'input dell'utente, consigliamo di utilizzare sempre il metodo nextLine della classeScanner
, per non incorrere in strani comportamenti. Infatti, l'utilizzo congiunto dinextLine
, e di altri metodi comenext
, sullo stesso oggettoScanner
, comporta che la lettura debba essere gestita in maniera più complessa. Quindi utilizzare sempre e solamente il metodonextLine
.
Soluzione
Abbiamo deciso di creare l'eccezione
ComandoNonValidoException
estendendoIOException
nel seguente modo:
package rubrica.eccezioni; import java.io.IOException; public class ComandoNonValidoException extends IOException { private static final long serialVersionUID = 6742493040156525789L; public ComandoNonValidoException(String comando){ super("Comando non valido:" + comando); } }
Ecco invece l'implementazione della classeRubricaCLI
:
package rubrica.presentation; import rubrica.dati.*; import rubrica.eccezioni.*; import java.util.*; import java.io.*; public class RubricaCLI { private Scanner scanner; private static final String INSERT ="/i"; private static final String SHOW_CONTACT ="/v"; public RubricaCLI() { scanner = new Scanner(System.in); } public void visualizzaContatti(List<Contatto> contatti) { try { int numeroContatti = contatti.size(); if (numeroContatti == 0) { System.out.println("Nessun contatto trovato"); } else { System.out.println("Lista dei contatti in Rubrica:"); for (Contatto contatto : contatti) { System.out.println(contatto.getNome()); } System.out.printf("Scrivi '%s' seguito dal nome di un contatto," + " per visualizzarne i dettagli\n", SHOW_CONTACT); System.out.printf("Scrivi '%s' per inserire un nuovo" + " contatto\n", INSERT); String comando = scanner.nextLine(); if (comando.equals(INSERT)) { System.out.println("Chiamato metodo inserisci contatto"); } else if (isComandoValidoPerVisualizzaContatto(contatti, comando)) { System.out.printf("Chiamato metodo visualizza contatto" + " per %s\n", estraiNomeContatto(comando)); } else { throw new ComandoNonValidoException(comando); } } } catch (Exception exc) { System.out.println(exc.getMessage()); } } private boolean isComandoValidoPerVisualizzaContatto( List<Contatto> contatti, String comando) throws ContattoInesistenteException, ComandoNonValidoException { boolean result = false; if (comando.startsWith(SHOW_CONTACT)) { for (Contatto contatto : contatti) { if (comando.endsWith(contatto.getNome())) { return true; } } } else { throw new ComandoNonValidoException(comando); } throw new ContattoInesistenteException(String.format( "Contatto %s non trovato!", estraiNomeContatto(comando))); } public String estraiNomeContatto(String comando) { return comando.substring(comando.indexOf(" ")+1, comando.length()); } }
Infine, segue la classeEsercizio15N
:
package rubrica.test; import rubrica.presentation.*; import rubrica.dati.*; import rubrica.eccezioni.*; import java.util.*; public class Esercizio15N { private void eseguiTest() { RubricaCLI cli = new RubricaCLI(); cli.visualizzaContatti(getContatti()); } private List<Contatto> getContatti() { List<Contatto> contatti = new ArrayList<>(); Contatto contatto1 = new Contatto("Daniele", "01234560", "Via delle chitarre 1"); Contatto contatto2 = new Contatto("Giovanni", "0565432190", "Via delle scienze 2"); Contatto contatto3 = new Contatto("Ligeia", "07899921", "Via dei segreti 3"); contatti.add(contatto1); contatti.add(contatto2); contatti.add(contatto3); return contatti; } public static void main(String args[]) { Esercizio15N esercizio15N = new Esercizio15N(); esercizio15N.eseguiTest(); } }
-
Esercizio 15.o)
Partendo dalla soluzione dell'esercizio precedente, iniziamo a creare la classe
RubricaController
, che si occuperà di svolgere il ruolo del componenteController
all'interno dell'architettura MVC. In particolare, tale classe ha il compito di definire la cosiddetta logica di controllo, ovvero dovrà implementare le seguenti responsabilità:
• ricevere ed interpretare gli input dell'utente notificati dal componente View;
• invocare metodi di business esposti dal componente Model (sia aggiornando che recuperando dati);
• selezionare la giusta schermata del componente View.Detto questo, nell'esercizio precedente abbiamo creato il metodo
visualizzaContatti
che si limitava a stampare determinati messaggi. È arrivato il momento di sostituire quei messaggi con notifiche al componente Controller (ovvero la View deve chiamare metodi sulla classeRubricaController
). Tali metodi, come abbiamo già detto, vengono rappresentati nel diagramma della figura 15.n.1 come le transizioni che portano da una schermata ad un'altra. I metodi diRubricaController
cominceranno sempre con la parola "gestisci".
Con l'esercizio precedente abbiamo invocato dalla classeEsercizio15N
il metodovisualizzaContatti
della classeRubricaCLI
. La classeEsercizio15N
si occupava di passare la metodovisualizzaContatti
anche la lista dei contatti. In questo esercizio invece, la classeEsercizio15O
invocherà un nuovo metodo della classeRubricaCLI
che chiameremostart
. Questo metodo a sua volta invocherà il metodostart
della classeRubricaController
. Qui, per rispettare le responsabilità del componente Controller, verrà invocato un metodo chiamatogetContatti
(da creare) del Model che restituirà tutti i contatti esistenti. Una volta che i contatti sono stati restituiti dal metodogetContatti
, il metodostart
della classeRubricaController
invocherà il metodovisualizzaContatti
della classeRubricaCLI
. Tutto questo è riassunto nel sequence diagram riportato in figura 15.o.1:
Figura 15.o.1 – Diagramma dei sequenza.
Quindi in questo esercizio dovremo:
• Creare il metodogetContatti
nel componente Model, che restituisce i contatti già creati. Notare che inizialmente non avremo contatti da recuperare, quindi per testare il metodogetContatti
, senza dover aspettare il completamento del prossimo esercizio, è possibile utilizzare i file .con creati negli esercizi precedenti (copiandoli nella vostra cartella di lavoro attuale), oppure creare un client di test che inserisca qualche contatto come fatto nei precedenti esercizi.
• Creare la classeRubricaController
come classe innestata nella classeRubricaCLI
(in questo modo risparmieremo diverse righe di codice).
• Creeremo il metodostart
nella classeRubricaController
. Questo deve chiamare il metodogetContatti
del componente Model, e una volta ottenuti i contatti, deve passarli al metodovisualizzaContatti
diRubricaCLI
. Nell'esercizio precedente avevamo chiamato dalla classeEsercizio15N
il metodovisualizzaContatti
della classeRubricaCLI
. In quel caso la classeEsercizio15N
si occupava anche di creare la lista dei contatti da passare al metodovisualizzaContatti
. Ora invece sarà il metodo start nella classeRubricaController
a recuperare i contatti e a passarli al metodovisualizzaContatti
della classeRubricaCLI
.
• Creare il metodostart
nella classeRubricaCLI
che invocherà il metodostart
della classeRubricaController
.
• Creare la classeEsercizio15O
che invoca il metodostart
della classeRubricaCLI
per fa partire l'applicazione.
Soluzione
Per prima cosa iniziamo a modificare il Model, aggiungendo all'interfaccia
GestoreSerializzazione
il nuovo metodogetContatti
:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; public interface GestoreSerializzazione<T extends Dato> { void inserisci(T dato) throws IOException; T recupera(String id) throws IOException, ClassNotFoundException; void modifica(T dato) throws IOException; void rimuovi(String id) throws IOException; List<Contatto> getContatti() throws IOException; }
Poi dobbiamo implementare questo metodo nelle sue due implementazioniGestoreFile
eGestoreFileNIO2
. Segue la nuova versione della classeGestoreFile
:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; public class GestoreFile implements GestoreSerializzazione<Contatto> { @Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Contatto contattoEsistente = getContatto( FileUtils.getNomeFile(contatto.getNome())); if (contattoEsistente != null) { throw new ContattoEsistenteException(contatto.getNome() + ": contatto già esistente!"); } registra( contatto); } @Override public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { Contatto contatto = getContatto(FileUtils.getNomeFile(nome)); if (contatto == null) { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } return contatto; } @Override public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { if (isContattoEsistente(contatto.getNome())) { registra(contatto); } else { throw new ContattoInesistenteException(contatto.getNome() + ": contatto non trovato!"); } } @Override public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { File file = new File(FileUtils.getNomeFile(nome)); if (file.delete()) { System.out.println("Contatto "+ nome +" cancellato!"); } else { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } } private void registra(Contatto contatto) throws FileNotFoundException, IOException { try (FileOutputStream fos = new FileOutputStream (new File( FileUtils.getNomeFile(contatto.getNome()))); ObjectOutputStream s = new ObjectOutputStream (fos);) { s.writeObject (contatto); System.out.println("Contatto registrato:\n"+ contatto); } } private boolean isContattoEsistente(String nome) { File file = new File(FileUtils.getNomeFile(nome)); return file.exists(); } private Contatto getContatto(String nome) { try (FileInputStream fis = new FileInputStream (new File(nome)); ObjectInputStream ois = new ObjectInputStream (fis);) { Contatto contatto = (Contatto)ois.readObject(); System.out.println("Contatto recuperato:\n"+ contatto); return contatto; } catch (Exception exc) { return null; } } @Override public List<Contatto> getContatti() throws IOException { List<Contatto> contatti = new ArrayList<Contatto>(); File directory = new File("."); for ( File file : directory.listFiles()) { if (file.isFile()) { String fileName = file.getName(); if (fileName.endsWith(".con")) { contatti.add(getContatto(fileName)); } } } return contatti; } }
Mentre la seguente è la nuova versione della classeGestoreFileNIO2
:
package rubrica.integrazione; import java.util.*; import java.io.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; import java.util.stream.*; import java.nio.file.*; public class GestoreFileNIO2 implements GestoreSerializzazione<Contatto> { @Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(contatto.getNome())); if (Files.exists(path)) { throw new ContattoEsistenteException(contatto.getNome() + ": contatto già esistente!"); } registra(contatto); } @Override public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { Contatto contatto = getContatto(FileUtils.getNomeFile(nome)); if (contatto == null) { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } return contatto; } @Override public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { if (isContattoEsistente(contatto.getNome())) { registra(contatto); } else { throw new ContattoInesistenteException(contatto.getNome() + ": contatto non trovato!"); } } @Override public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(nome)); if (Files.exists(path)) { Files.delete(path); System.out.println("Contatto "+ nome +" cancellato!"); } else { throw new ContattoInesistenteException(nome + ": contatto non trovato!"); } } private void registra(Contatto contatto) throws FileNotFoundException, IOException { Path path = Paths.get(FileUtils.getNomeFile(contatto.getNome())); Files.write(path, getBytesDaOggetto(contatto)); System.out.println("Contatto registrato:\n"+ contatto); } private byte[] getBytesDaOggetto(Object object) throws IOException { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) { out.writeObject(object); return bos.toByteArray(); } } private Object getOggettoDaByte(byte[] bytes) throws IOException, ClassNotFoundException { try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) { return in.readObject(); } } private boolean isContattoEsistente(String nome) { Path path = Paths.get(FileUtils.getNomeFile(nome)); return Files.exists(path); } private Contatto getContatto(String nome) { Path path = Paths.get(nome); byte[] bytes = null; Contatto contatto = null; try { bytes = Files.readAllBytes(path); contatto = (Contatto)getOggettoDaByte(bytes); System.out.println("Contatto recuperato:\n"+ contatto); } catch (Exception exc) { return null; } return contatto; } @Override public List<Contatto> getContatti() throws IOException { List<Contatto> contatti = new ArrayList<>(); try (Stream<Path> walk = Files.walk(Paths.get("."))) { contatti = walk.map(p -> p.toString()). filter(f -> f.endsWith(".con")).map(f -> getContatto(f)). collect(Collectors.toList()); } return contatti; } }
Segue la nuova versione della classeRubricaCLI
, che contiene la classe innestataRubricaController
:
package rubrica.presentation; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import java.util.*; import java.io.*; public class RubricaCLI { private Scanner scanner; private RubricaController controller; private static final String INSERT ="/i"; private static final String SHOW_CONTACT ="/v"; public RubricaCLI() { scanner = new Scanner(System.in); controller = new RubricaController(); } public void start() { try { controller.gestisciVisualizzaContatti(); } catch (IOException exc) { System.out.println(exc.getMessage()); } } public void visualizzaContatti(List<Contatto> contatti) { try { int numeroContatti = contatti.size(); if (numeroContatti == 0) { System.out.println("Nessun contatto trovato"); } else { System.out.println("Lista dei contatti in Rubrica:"); for (Contatto contatto : contatti) { System.out.println(contatto.getNome()); } System.out.printf("Scrivi '%s' seguito dal nome di un contatto," + " per visualizzarne i dettagli\n", SHOW_CONTACT); System.out.printf("Scrivi '%s' per inserire un nuovo " + "contatto\n", INSERT); String comando = scanner.nextLine(); if (comando.equals(INSERT)) { System.out.println("Chiamato metodo inserisci contatto"); } else if (isComandoValidoPerVisualizzaContatto(contatti, comando)) { System.out.printf("Chiamato metodo visualizza contatto per" + " %s\n", estraiNomeContatto(comando)); } else { throw new ComandoNonValidoException(comando); } } } catch (Exception exc) { System.out.println(exc.getMessage()); } } private boolean isComandoValidoPerVisualizzaContatto(List<Contatto> contatti, String comando) throws ContattoInesistenteException, ComandoNonValidoException { boolean result = false; if (comando.startsWith(SHOW_CONTACT)) { for (Contatto contatto : contatti) { if (comando.endsWith(contatto.getNome())) { return true; } } } else { throw new ComandoNonValidoException(comando); } throw new ContattoInesistenteException(String.format( "Contatto %s non trovato!", estraiNomeContatto(comando))); } public String estraiNomeContatto(String comando) { return comando.substring(comando.indexOf(" ")+1, comando.length()); } class RubricaController { GestoreSerializzazione gestoreFile; public RubricaController () { try { gestoreFile = GestoreSerializzazioneFactory.getGestoreSerializzazione(); } catch (Exception exc) { exc.printStackTrace(); System.exit(1); } } public void gestisciVisualizzaContatti() throws IOException { List<Contatto> contatti = gestoreFile.getContatti(); visualizzaContatti(contatti); } } }
-
Esercizio 15.p)
Partendo dalla soluzione dell'esercizio precedente, implementare il flusso di inserimento di un contatto come descritto nel diagramma mostrato in figura 15.n.2. In particolare:
• Creare il metodogestisciVisualizzaContatti
nella classeRubricaController
, che deve prendere in input il comando inserito dall'utente, e la lista dei contatti. Tale metodo deve essere invocato nel metodovisualizzaContatti
(che va cambiato completamente) quando l'utente ha inserito un comando, e deve avere la responsabilità di interpretare questo comando e invocare i giusti metodi. In particolare, deve eseguire il flusso condizionale che avevamo definito nel metodovisualizzaContatti
nell'esercizio precedente, con l'eccezione del caso dell'inserimento. Infatti se è stato inserito il comando "/i
" allora deve essere invocare il metodoinserisciContatto
della classeRubricaCLI
. Per il resto il codice per ora non deve cambiare nulla. In pratica bisogna spostare parte del codice che avevamo nel metodovisualizzaContatti
della classeRubricaCLI
, nel metodogestisciVisualizzaContatti
della classeRubricaController
, modificando la condizione relativo all'inserimento.
• Modificare il metodovisualizzaContatti
in modo tale che dopo aver catturato il comando dell'utente, ne deleghi l'interpretazione la metodogestisciVisualizzaContatti
della classeRubricaController
, ovvero deve invocare questo metodo passandogli il comando inserito e la lista dei contatti.
• Creare il metodoinserisciContatto
nella classeRubricaCLI
. Tale metodo deve richiedere all'utente prima l'inserimento del nome, poi del numero di telefono, e infine dell'indirizzo. Dopo aver raccolto le informazioni del contatto, deve offrire l'opportunità all'utente di inserire il comando "/e
" per confermare l'inserimento, o il comando "/b
" per tornare alla visualizzazione dei contatti (vedi figura 15.n.2). Una volta recuperato il comando inserito dall'utente, deve invocare il metodogestisciInserisciContatto
nella classeRubricaController
, che deve avere la responsabilità di interpretare tale comando.
• Creare il metodogestisciInserisciContatto
nella classeRubricaController
. Tale metodo deve prendere in input il comando inserito dall'utente, il nome, il numero di telefono e l'indirizzo del contatto. Questo metodo deve avere la responsabilità di interpretare il comando inserito e deve essere invocato nel metodoinserisciContatto
che aveva la responsabilità di recuperare l'input dell'utente. In particolare, nel caso l'utente abbia inserito il comando "/b
" bisogna invocare nuovamente il metodovisualizzaContatti
della classeRubricaCLI
. Nel caso l'utente abbia inserito il comando "/e
" bisogna invocare il metodo inserisci del componente Model, senza gestire eventuali eccezioni, che invece devono essere gestite direttamente dai metodi diRubricaCLI
, come già detto in precedenza. Infine deve invocare il metodovisualizzaConferma
della classeRubricaCLI
.
• Non deve essere possibile creare un contatto senza nome. Creare quindi un'eccezione chiamataNomeVuotoException
, da lanciare nel metodo più opportuno.
• Creare il metodovisualizzaConferma
della classeRubricaCLI
. Questo metodo deve essere inteso come generico e riutilizzabile, deve stampare la frase "Operazione confermata!
", e deve prendere in input il messaggio che deve mostrare subito dopo (per esempio potremmo passare l'istruzione"Inserito contatto:\n"+ contatto.toString
). Infine deve permettere all'utente di inserire i comandi descritti nel diagramma dalla figura 15.n.2, ovvero i comandi "/b
" per tornare alla visualizzazione dei contatti e "/t
" per terminare il programma. Dopo aver catturato il comando dell'utente deve esserne delegata l'interpretazione al metodogestisciVisualizzaMessaggio
della classeRubricaController
. Stesso discorso per un metodo generico che si chiamavisualizzaErrore
, che può essere chiamato in tutte le clausolecatch
dei metodi diRubricaCLI
, per ottenere una gestione centralizzata delle eccezioni.
• Il metodogestisciVisualizzaMessaggio
della classeRubricaController
, deve prendere in input il comando recuperato dal metodovisualizzaConferma
della classeRubricaCLI
. Seguendo il diagramma in figura 15.n.2, questo metodo deve invocare il metodovisualizzaContatti
se il comando inoltrato dall'utente è "/b
", oppure terminare il programma se il comando inoltrato dall'utente è "/t
".
• Creare la classeEsercizio15P
a partire dalla classeEsercizio15Q
per testare l'applicazione.
Soluzione
Per prima cosa, abbiamo definito l'eccezione
NomeVuotoException
richiesta in questo modo:
package rubrica.eccezioni; public class NomeVuotoException extends Exception { private static final long serialVersionUID = 8290498479156525745L; public NomeVuotoException() { super("Il nome del contatto non può essere vuoto!"); } }
Ovviamente abbiamo implementato il controllo affinché il nome di un contatto non sia vuoto, all'interno della classeContatto
sfruttando l'incapsulamento:
package rubrica.dati; import rubrica.eccezioni.*; public class Contatto extends Entita { protected static final String SCONOSCIUTO ="sconosciuto"; private String nome; private String numeroDiTelefono; private String indirizzo; public Contatto(String nome, String numeroDiTelefono) throws NomeVuotoException { this(nome, numeroDiTelefono, SCONOSCIUTO); } public Contatto(String nome, String numeroDiTelefono, String indirizzo) throws NomeVuotoException { super(); this.setNome(nome); this.setNumeroDiTelefono(numeroDiTelefono); this.setIndirizzo(indirizzo); } public void setIndirizzo(String indirizzo) { this.indirizzo = indirizzo; } public String getIndirizzo() { return indirizzo; } public void setNumeroDiTelefono(String numeroDiTelefono) { this.numeroDiTelefono = numeroDiTelefono; } public String getNumeroDiTelefono() { return numeroDiTelefono; } public void setNome(String nome) throws NomeVuotoException { if (nome == null || nome.trim().length() == 0) { throw new NomeVuotoException(); } this.nome = nome; } public String getNome() { return nome; } @Override public String toString() { return"Nome:\t"+ nome +"\nIndirizzo:\t"+ indirizzo +"\nTelefono:\t"+ numeroDiTelefono; } }
Per quanto riguarda le modifiche delle classiRubricaCLI
eRubricaController
richieste:
package rubrica.presentation; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import java.util.*; import java.io.*; public class RubricaCLI { private Scanner scanner; private RubricaController controller; private static final String INSERT ="/i"; private static final String SHOW_CONTACT ="/v"; private static final String EXECUTE ="/e"; private static final String BACK ="/b"; private static final String END ="/t"; public RubricaCLI() { scanner = new Scanner(System.in); controller = new RubricaController(); } public void start() { try { controller.start(); } catch (IOException exc) { System.out.println(exc.getMessage()); } } public void visualizzaContatti(List<Contatto> contatti) { try { int numeroContatti = contatti.size(); if (numeroContatti == 0) { System.out.println("Nessun contatto trovato"); } else { System.out.println("Lista dei contatti in Rubrica:"); int size = contatti.size(); for (int i = 1; i <= size; ++i) { System.out.println(i +"\t"+contatti.get(i-1).getNome()); } System.out.printf("Scrivi '%s' e il nome del contatto per " + " visualizzarne i dettagli\n", SHOW_CONTACT); } System.out.printf("Scrivi '%s' per inserire un nuovo contatto\n", INSERT); System.out.printf("Scrivi '%s' per terminare il programma\n", END); String comando = scanner.nextLine(); controller.gestisciVisualizzaContatti(comando, contatti); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void inserisciContatto() { try { String nome = getDatoContatto("nome"); String numeroDiTelefono = getDatoContatto("numero di telefono"); String indirizzo = getDatoContatto("indirizzo"); System.out.printf("Scrivi '%s' per confermare l'inserimento\n", EXECUTE); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); String comando = scanner.nextLine(); controller.gestisciInserisciContatto(comando, nome, numeroDiTelefono, indirizzo); } catch (Exception exc) { System.out.println(exc.getMessage()); } } public void visualizzaConferma(String messaggio) { try { System.out.println("Operazione confermata!"); visualizzaMessaggio(messaggio); } catch (Exception exc) { System.out.println(exc.getMessage()); } } private void visualizzaMessaggio(String messaggio) throws IOException, ComandoNonValidoException{ System.out.println(messaggio +"\n"); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei " + "contatti\n", BACK); System.out.printf("Scrivi '%s' per terminare il programma\n", END); String comando = scanner.nextLine(); controller.gestisciVisualizzaConferma(comando); } private String getDatoContatto(String dato) { String messaggio = String.format("Inserisci %s del contatto:", dato); return leggiInputUtente(messaggio); } private String leggiInputUtente(String messaggio) { System.out.printf(messaggio); return scanner.nextLine(); } class RubricaController { GestoreSerializzazione gestoreFile; public RubricaController () { try { gestoreFile = GestoreSerializzazioneFactory.getGestoreSerializzazione(); } catch (Exception exc) { exc.printStackTrace(); System.exit(1); } } public void start() throws IOException { List<Contatto> contatti = gestoreFile.getContatti(); visualizzaContatti(contatti); } public void gestisciVisualizzaContatti(String comando, List<Contatto> contatti) throws IOException { if (comando.equals(INSERT)) { //System.out.println("Chiamato metodo inserisci contatto"); inserisciContatto(); } else if (comando.equals(END)) { System.out.println("Programma terminato"); System.exit(1); } else if (isComandoValidoPerVisualizzaContatto(contatti, comando)){ System.out.printf("Chiamato metodo visualizza contatto per " + "%s\n", estraiNomeContatto(comando)); } else { throw new ComandoNonValidoException(comando); } } public void gestisciInserisciContatto(String comando, String nome, String numeroDiTelefono, String indirizzo) throws IOException, NomeVuotoException { if (comando.equals(EXECUTE)) { Contatto contatto = new Contatto(nome, numeroDiTelefono, indirizzo); gestoreFile.inserisci(contatto); visualizzaConferma("Inserito contatto:\n"+ contatto); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } public void gestisciVisualizzaConferma(String comando) throws IOException, ComandoNonValidoException { if (comando.equals(END)) { System.out.println("Programma terminato"); System.exit(1); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } private boolean isComandoValidoPerVisualizzaContatto(List<Contatto> contatti, String comando) throws ContattoInesistenteException, ComandoNonValidoException { boolean result = false; if (comando.startsWith(SHOW_CONTACT)) { for (Contatto contatto : contatti) { if (comando.endsWith(contatto.getNome())) { return true; } } } else { throw new ComandoNonValidoException(comando); } throw new ContattoInesistenteException(String.format("Contatto %s " + "non trovato!", estraiNomeContatto(comando))); } public String estraiNomeContatto(String comando) { return comando.substring(comando.indexOf(" ")+1, comando.length()); } } }
Infine segue la classeEsercizio15P
:
package rubrica.test; import rubrica.presentation.*; import java.util.*; public class Esercizio15P { private void eseguiTest() { RubricaCLI cli = new RubricaCLI(); cli.start(); } public static void main(String args[]) { Esercizio15P esercizio15P = new Esercizio15P(); esercizio15P.eseguiTest(); } }
-
Esercizio 15.q)
Partendo dalla soluzione dell'esercizio precedente, implementare i flussi di modifica e cancellazione di un contatto come descritto nel diagramma mostrato in figura 15.n.2. In particolare:
• Modificare il metodogestisciVisualizzaContatti
nella classeRubricaController
, che prende in input il comando inserito dall'utente, e la lista dei contatti. Se l'utente specifica il comando "/v
" seguito dal nome di un contatto esistente, sostituire l'attuale metodo di stampa, con la chiamata al metodo di un nuovo metodo:visualizzaContatto
della classeRubricaCLI
, che prende in input il contatto specificato.
• Creare il metodovisualizzaContatto
della classeRubricaCLI
, che deve quindi mostrare i dettagli del contatto selezionato e proporre all'utente la possibilità di tornare indietro a visualizzare tutti i contatti specificando il comando "/b
", oppure specificare il comando "/u
" per modificare il contatto selezionato, o specificare il comando "/r
" per rimuovere il contatto selezionato. Poi deve catturare il comando e delegarne l'interpretazione al metodogestisciVisualizzaContatto
della classeRubricaController
che prende in input il comando e il contatto.
• Creare il metodogestisciVisualizzaContatto
che deve avere la responsabilità di interpretare il comando inserito. In particolare, nel caso l'utente abbia inserito il comando "/b
" bisogna invocare nuovamente il metodovisualizzaContatti
della classeRubricaCLI
. Nel caso l'utente abbia inserito il comando "/u
" bisogna invocare il metodomodificaContatto
della classeRubricaCLI
. Se invece viene l'utente inserisce il comando "/r
", allora deve esser invocato il metodorimuoviContatto
della classeRubricaCLI
.
• Creare il metodomodificaContatto
della classeRubricaCLI
che deve prendere in input il contatto selezionato. Esso deve chiedere all'utente di specificare un nuovo numero di telefono, e un nuovo indirizzo. Poi indicare all'utente la possibilità all'utente di scegliere tra il comando "/b
" per ritornare a vedere tutti contatti, o il comando "/e
" per eseguire l'aggiornamento. Infine deve catturare il comando dell'utente e delegarne l'interpretazione al componente Controller, invocando il metodogestisciModificaContatto
passando ad esso il comando, e il nome, il numero di telefono e l'indirizzo del contatto.
• Creare il metodogestisciModificaContatto
della classeRubricaController
per interpretare il comando da eseguire. In particolare, esso, oltre a gestire come al solito il comando "/b
" per tornare a vedere tutti i contatti della rubrica, nel caso l'utente abbia specificato il comando "/e
" deve invocare il metodo modifica sul componente Model, per poi invocare il metodovisualizzaConferma
della classeRubricaCLI
.
• Creare il metodorimuoviContatto
della classeRubricaCLI
che deve prendere in input il nome del contatto selezionato. Esso deve indicare all'utente la possibilità di scegliere tra il comando "/b
" per ritornare a vedere tutti contatti, o il comando "/e
" per eseguire la rimozione del contatto. Infine deve catturare il comando dell'utente e delegarne l'interpretazione alla classeRubricaController
, invocando il metodogestisciRimuoviContatto
passando ad esso il comando e il nome del contatto.
• Creare il metodogestisciRimuoviContatto
della classeRubricaController
per interpretare il comando da eseguire. In particolare, esso, oltre a gestire come al solito il comando "/b
" per tornare a vedere tutti i contatti della rubrica, nel caso l'utente abbia specificato il comando "/e
" deve invocare il metodorimuovi
sul componente Model, per poi invocare il metodovisualizzaConferma
della classeRubricaCLI
.
• Creare la classeEsercizio15Q
a partire dalla classeEsercizio15P
per testare l'applicazione.
Soluzione
Le classi
RubricaCLI
eRubricaController
sono state modificate nel seguente modo:
package rubrica.presentation; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.integrazione.*; import java.util.*; import java.io.*; public class RubricaCLI { private Scanner scanner; private RubricaController controller; private static final String INSERT ="/i"; private static final String SHOW_CONTACT ="/v"; private static final String UPDATE_CONTACT ="/u"; private static final String REMOVE_CONTACT ="/r"; private static final String EXECUTE ="/e"; private static final String BACK ="/b"; private static final String END ="/t"; public RubricaCLI() { scanner = new Scanner(System.in); controller = new RubricaController(); } public void start() { try { controller.start(); } catch (IOException exc) { visualizzaErrore(exc); } } public void visualizzaContatto(Contatto contatto) { try { System.out.println("Dettagli del contatto selezionato:\n" + contatto); System.out.printf("Scrivi '%s' per modificare il contatto\n", UPDATE_CONTACT); System.out.printf("Scrivi '%s' per rimuovere il contatto\n", REMOVE_CONTACT); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); String comando = scanner.nextLine(); controller.gestisciVisualizzaContatto(comando, contatto); } catch (Exception exc) { visualizzaErrore(exc); } } public void modificaContatto(Contatto contatto) { try { String numeroDiTelefono = getDatoContatto("numero di telefono"); String indirizzo = getDatoContatto("indirizzo"); System.out.printf("Scrivi '%s' per confermare la modifica\n", EXECUTE); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); String comando = scanner.nextLine(); controller.gestisciModificaContatto(comando, contatto.getNome(), numeroDiTelefono, indirizzo); } catch (Exception exc) { visualizzaErrore(exc); } } public void rimuoviContatto(Contatto contatto) { try { System.out.printf("Scrivi '%s' per confermare la rimozione\n", EXECUTE); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); String comando = scanner.nextLine(); controller.gestisciRimuoviContatto(comando, contatto.getNome()); } catch (Exception exc) { visualizzaErrore(exc); } } public void visualizzaContatti(List<Contatto> contatti) { try { int numeroContatti = contatti.size(); if (numeroContatti == 0) { System.out.println("Nessun contatto trovato"); } else { System.out.println("Lista dei contatti in Rubrica:"); int size = contatti.size(); for (int i = 1; i <= size; ++i) { System.out.println(i +"\t"+contatti.get(i-1).getNome()); } System.out.printf("Scrivi '%s' e il nome del contatto per " + "visualizzarne i dettagli\n", SHOW_CONTACT); } System.out.printf("Scrivi '%s' per inserire un nuovo contatto\n", INSERT); System.out.printf("Scrivi '%s' per terminare il programma\n", END); String comando = scanner.nextLine(); controller.gestisciVisualizzaContatti(comando, contatti); } catch (Exception exc) { visualizzaErrore(exc); } } public void inserisciContatto() { try { String nome = getDatoContatto("nome"); String numeroDiTelefono = getDatoContatto("numero di telefono"); String indirizzo = getDatoContatto("indirizzo"); System.out.printf("Scrivi '%s' per confermare l'inserimento\n", EXECUTE); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); String comando = scanner.nextLine(); controller.gestisciInserisciContatto(comando, nome, numeroDiTelefono, indirizzo); } catch (Exception exc) { visualizzaErrore(exc); } } public void visualizzaConferma(String messaggio) { System.out.println("Operazione confermata!"); visualizzaMessaggio(messaggio); } public void visualizzaErrore(Exception exc) { System.out.println("C'è un problema!"); visualizzaMessaggio(exc.toString()); } private void visualizzaMessaggio(String messaggio) { try { System.out.println(messaggio +"\n"); System.out.printf("Scrivi '%s' per tornare alla visualizzazione dei" + " contatti\n", BACK); System.out.printf("Scrivi '%s' per terminare il programma\n", END); String comando = scanner.nextLine(); controller.gestisciVisualizzaMessaggio(comando); } catch (Exception exc) { visualizzaMessaggio(messaggio); } } private String getDatoContatto(String dato) { String messaggio = String.format("Inserisci %s del contatto:", dato); return leggiInputUtente(messaggio); } private String leggiInputUtente(String messaggio) { System.out.printf(messaggio); return scanner.nextLine(); } class RubricaController { GestoreSerializzazione<Contatto> gestoreFile; public RubricaController() { try { gestoreFile = GestoreSerializzazioneFactory.getGestoreSerializzazione(); } catch (Exception exc) { exc.printStackTrace(); System.exit(1); } } public void start() throws IOException { List<Contatto> contatti = gestoreFile.getContatti(); visualizzaContatti(contatti); } public void gestisciVisualizzaContatti(String comando,List<Contatto> contatti) throws IOException, ClassNotFoundException { if (comando.equals(INSERT)) { //System.out.println("Chiamato metodo inserisci contatto"); inserisciContatto(); } else if (comando.equals(END)) { System.out.println("Programma terminato"); System.exit(1); } else if (isComandoValidoPerVisualizzaContatto(contatti, comando)){ String nomeContatto = estraiNomeContatto(comando); System.out.printf("Chiamato metodo visualizza contatto per " + "%s\n", nomeContatto); Contatto contatto = gestoreFile.recupera(nomeContatto); visualizzaContatto(contatto); } else { throw new ComandoNonValidoException(comando); } } public void gestisciVisualizzaContatto(String comando,Contatto contatto) throws IOException { if (comando.equals(UPDATE_CONTACT)) { modificaContatto(contatto); } else if (comando.equals(REMOVE_CONTACT)) { rimuoviContatto(contatto); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } public void gestisciInserisciContatto(String comando, String nome, String numeroDiTelefono, String indirizzo) throws IOException { if (comando.equals(EXECUTE)) { Contatto contatto = new Contatto(nome, numeroDiTelefono, indirizzo); gestoreFile.inserisci(contatto); visualizzaConferma("Inserimento confermato:\n"+ contatto); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } public void gestisciModificaContatto(String comando, String nome, String numeroDiTelefono, String indirizzo) throws IOException { if (comando.equals(EXECUTE)) { Contatto contatto = new Contatto(nome, numeroDiTelefono, indirizzo); gestoreFile.modifica(contatto); visualizzaConferma("Modifica confermata:\n"+ contatto); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } public void gestisciRimuoviContatto(String comando, String nome) throws IOException { if (comando.equals(EXECUTE)) { gestoreFile.rimuovi(nome); visualizzaConferma("Rimozione confermata:\n"+ nome); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } private boolean isComandoValidoPerVisualizzaContatto(List<Contatto> contatti, String comando) throws ContattoInesistenteException, ComandoNonValidoException { boolean result = false; if (comando.startsWith(SHOW_CONTACT)) { for (Contatto contatto : contatti) { if (comando.endsWith(contatto.getNome())) { return true; } } } else { throw new ComandoNonValidoException(comando); } throw new ContattoInesistenteException(String.format("Contatto %s " + "non trovato!", estraiNomeContatto(comando))); } public void gestisciVisualizzaMessaggio(String comando) throws IOException{ if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else if (comando.equals(BACK)) { visualizzaContatti(gestoreFile.getContatti()); } else { throw new ComandoNonValidoException(comando); } } public String estraiNomeContatto(String comando) { return comando.substring(comando.indexOf(" ")+1, comando.length()); } } }
Ovviamente la classeEsercizio15Q
è la seguente:
package rubrica.test; import rubrica.presentation.*; import java.util.*; public class Esercizio15Q { private void eseguiTest() { RubricaCLI cli = new RubricaCLI(); cli.start(); } public static void main(String args[]) { Esercizio15Q esercizio15Q = new Esercizio15Q(); esercizio15Q.eseguiTest(); } }
-
Esercizio 15.r)
Quali delle seguenti affermazioni sono corrette?
1. La classeFile
definisce un metodogetPath
che restituisce una stringa.
2. La classeFile
definisce un metodogetParent
che restituisce un oggettoFile
che rappresenta una directory.
3.pathSeparator
è una costante statica della classeFile
, che rappresenta il separatore di path dipendente dal sistema operativo.
4. La classeFile
definisce un metododelete
che restituisce un booleanotrue
se il file sarà davvero eliminato, oppurefalse
in caso contrario.
Soluzione
Le affermazioni corrette sono la prima e la quarta. L'affermazione numero 2 è scorretta in quanto
getParent
restituisce una stringa che rappresenta una directory. L'affermazione numero 3 è scorretta in quantopathSeparator
è una variabile statica della classeFile
, che rappresenta il separatore di path dipendente dal sistema operativo. Lo si poteva evincere sia dal nome che è scritto in minuscolo (le costanti invece per convenzione sono scritte in maiuscolo), sia dal fatto che non può assumere un valore a priori visto che esso dipende dal sistema operativo. -
Esercizio 15.s)
Quali delle seguenti affermazioni sono corrette?
1. Le implementazioni diInputStream
sono ottimizzate per leggere byte, le implementazioni diOutputStream
sono ottimizzate per scrivere byte. Infatti vengono dette "byte stream".
2. Le classi alla base della gerarchia dei "character stream" sonoReader
eWriter
.
3. Il metodoreadLine
è definito nella classeReader
.
4. Il costruttore di unObjectInputStream
deve prendere in input un oggetto di tipoFileInputStream
.
Soluzione
L'unica affermazione corretta è la prima. L'affermazione numero 2 è scorretta in quanto
Reader
eWriter
sono interfacce e non classi. L'affermazione numero 3 è scorretta in quanto il metodoreadLine
è definito all'interno della classeBufferedReader
. L'affermazione numero 4 è scorretta in quanto il costruttore di unObjectInputStream
può prendere in input un qualsiasi oggetto di tipoInputStream
. -
Esercizio 15.t)
Quali delle seguenti affermazioni sono corrette?
1. Se istanziamo un oggettoFile
specificando come parametro del costruttore il nome di un file che non esiste, esso sarà creato.
2. Per leggere il contenuto di un file di testo, basta utilizzare la classeFileReader
.
3. Una directory può essere creata grazie alla classeFile
.
4. La classeFile
definisce un metodo che restituisce i nomi dei file all'interno di una directory.
Soluzione
L'unica affermazione scorretta è la numero 1. L'affermazione numero 2 è corretta anche se l'efficienza e la comodità della lettura verrebbero indubbiamente migliorate "agganciando" unBufferedReader
alFileReader
. Per esempio potremmo leggere un file con un codice simile al seguente:
Reader fileReader = new FileReader("c:/miofile.txt"); int data = fileReader.read(); while (data != -1) { System.out.print(data); data = fileReader.read(); } fileReader.close();
L'affermazione numero 3 è corretta, perché la classeFile
definisce il metodomkDir
, ed anche la 4 è corretta in quanto la classeFile
definisce il metodolist
. -
Esercizio 15.u)
Data la seguente classe:
import java.io.*; public class Esercizio15R { public static void main(String args[]) throws IOException { try (FileOutputStream fos = new FileOutputStream("nuovo file.txt"); DataOutputStream dos = new DataOutputStream(fos);) { dos.writeInt(8); dos.writeDouble(0.1176); } } }
Una volta creato il file nuovo
file.txt
, quanto spazio occuperà sull'hard disk?1. Non verrà creato nessun file perché il file non viene chiuso correttamente.
2. 12 byte.
3. Non verrà creato nessun file perché il file non compila.
4. 64 byte.
5. Non verrà creato nessun file perché durante l'esecuzione sarà lanciata un'eccezione.
6. 128 byte.
7. La dimensione del file dipende dalla piattaforma su cui viene eseguito il programma.
8. 96 byte.
Soluzione
La risposta corretta è la numero 2, ovvero 12 byte. Infatti vengono scritti all'interno del file un tipo
int
(32 bit = 4 byte) e undouble
(64 bit = 8 byte). -
Esercizio 15.v)
Data la seguente classe:
import java.io.*; public class Esercizio15S { public static void main(String args[]) throws Exception { try (FileOutputStream fos = new FileOutputStream("nuovo file.txt"); DataOutputStream dos = new DataOutputStream(fos);) { for (int i = 0; i < 50; i++) { dos.writeInt(i); } } } }
Scrivere la classe che legge il file creato.
Soluzione
La soluzione potrebbe essere la seguente:
import java.io.*; public class Soluzione15S { public static void main(String args[]) throws Exception { try (FileInputStream fis = new FileInputStream("nuovo file.txt"); DataInputStream dis = new DataInputStream(fis);) { for (int i = 0; i < 50; i++) { System.out.print(dis.readInt()); } } } }
Il cui output è:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
-
Esercizio 15.w)
Quali delle seguenti affermazioni sono corrette?
1. Per istanziare unReader
dobbiamo gestire unaIOException
.
2. Per scrivere da unOutputStream
dobbiamo gestire unaIOException
.
3. Per leggere da un oggettoInputStream
dobbiamo gestire unaIOException
.
4. Per istanziare un oggettoWriter
dobbiamo gestire unaFileNotFoundException
.
Soluzione
Le affermazioni corrette sono le numero 2 e 3. La 1 non è corretta perché esistono implementazioni di
Reader
, comeStringReader
, che non dichiarano clausolethrows
aIOException
. L'affermazione 4 non è corretta in quanto devono gestire tali implementazioni solo le implementazioni diWriter
che hanno a che fare con i file comeFileWriter
.
-
Esercizio 15.x)
Quali delle seguenti affermazioni sono corrette?
1. È importante chiudere uno stream, altrimenti la memoria che occupa non sarà deallocata sino al termine del programma.
2. L'unica classe che mette a diposizione Java per leggere in maniera interattiva istruzioni da riga di comando, è la classeScanner
.
3. La classeBufferedReader
dichiara un metodo chiamatoreadLine
che legge una riga di testo intera dalla fonte passata in input alBufferedReader
.
4. La classeBufferedReader
dichiara un metodo chiamatolines
che restituisce un oggettoStream<String>
.
Soluzione
Le affermazioni corrette sono le numero 1, 3 e 4. L'affermazione 2 non è corretta perché abbiamo visto, anche nel paragrafo 15.4.1, che è possibile ottenere lo stesso risultato concatenando un
BufferedReader
ad unoInputStreamReader
la cui fonte di lettura è l'oggettoSystem.in
(ovvero il dispositivo di input di default, probabilmente la tastiera). Per comodità riportiamo di seguito la classe definita nel paragrafo 15.4.1.1KeyboardInput
:
import java.io.*; public class KeyboardInput { public static void main (String args[]) throws IOException { String stringa = null; System.out.println("Digita qualcosa e premi invio...\n" + "Per terminare il programma digitare \"fine\""); try (InputStreamReader ir = new InputStreamReader(System.in); BufferedReader in = new BufferedReader(ir)) { stringa = in.readLine(); while ( stringa != null ) { if (stringa.equals("fine")) { System.out.println("Programma terminato"); break; } System.out.println("Hai scritto: " + stringa); stringa = in.readLine(); } } } }
-
Esercizio 15.y)
Quali delle seguenti affermazioni sono corrette?
1.Path
definisce un metodo che restituisce i nomi dei file all'interno di una directory.
2.Path
dichiara un metodo per recuperare la directory in cui è contenuto.
3.Path
dichiara un metodo per recuperare l'autore (l'owner) del file.
4.Path
è un'interfaccia.
Soluzione
Solo le affermazioni 1 e 3 non sono corrette. In particolare l'affermazione 2 è corretta alla luce dell'esistenza del metodo
getParent
. La 3 invece non è corretta visto che il metodogetOwner
è definito dalla classeFiles
. -
Esercizio 15.z)
Quali delle seguenti affermazioni sono corrette?
1. La classeFiles
definisce un metodo che restituisce i nomi dei file all'interno di una directory.
2. Il metodofind
della classeFiles
restituisce unoStream
diPath
.
3.Files
ePath
appartengono al packagejava.nio2.file
.
4.Files
è un'interfaccia.
5. È possibile copiare un file in un altro con il metodocopy
.
Soluzione
Solo le affermazioni 2 e 5 sono corrette. L'affermazione 1 non è corretta perché, il metodo list di
Files
, restituisce unoStream
di oggettiPath
. La 3 non è corretta perché i tipiFiles
ePath
appartengono al packagejava.nio.file
. Mentre la 4 non è corretta perchéFiles
è una classe astratta. -
Esercizio 15.aa) Networking, Vero o Falso:
1. In una comunicazione di rete devono esistere almeno due socket.
2. Un client, per connettersi ad un server, deve conoscere almeno il suo indirizzo IP e la porta su cui si è posto in ascolto.
3. Un server si può mettere in ascolto anche sulla porta 80, la porta di default dell'HTTP, senza per forza utilizzare quel protocollo. È infatti possibile anche che si comunichi con il protocollo HTTP su una porta diversa dalla 80.
4. Il metodo accept blocca il server in uno stato di "attesa di connessioni". Quando un client si connette, il metodo accept viene eseguito per raccogliere tutte le informazioni del client in un oggetto di tipo Socket.
5. Un ServerSocket non ha bisogno di dichiarare l'indirizzo IP, ma deve solo dichiarare la porta su cui si aspetterà connessioni.
Soluzione
1. Vero.
2. Vero.
3. Vero.
4. Vero.
5. Vero. -
Esercizio I.a) Networking, Vero o Falso:
1. I metodi di tipo "
from
" permettono di recuperare un certo tipo temporale, a partire da un tipo temporale con più informazioni.
2. L'unico modo per fare il parsing di una stringa per ottenere un oggettoInstant
, è passare tramite la classeDateTimeFormatter
.
3. La classeDuration
calcola la distanza tra due istanti, quindi è definita sulla timeline.
4. Non è possibile memorizzare informazioni sull'orario in un oggetto di tipoLocalDate
.
5. È possibile memorizzare informazioni sulla data in un oggetto di tipoLocalTime
.
6. È possibile memorizzare informazioni sulla data in un oggetto di tipoZonedDate
.
7. Il metodobetween
diChronoUnit
restituisce un oggettoDuration
.
8. I temporaladjusters
possono essere passati a metodi di tipo "with
" per compiere operazioni su date e orari.
9. I temporal queries possono essere passati a metodi di tipo "with
" per recuperare informazioni su date e orari.
10. In generale è possibile sostituire la classeDate
con la classeInstant
.
Soluzione
1. Vero.
2. Falso, anche la stessa classeInstant
definisce un metodoparse
.
3. Falso,Duration
non è connessa alla timeline visto che rappresenta un intervallo di tempo compreso tra dueInstant
.
4. Vero.
5. Falso.
6. Falso, la classeZonedDate
semplicemente non esiste.
7. Falso.
8. Vero.
9. Falso.
10. Vero. -
Esercizio I.b)
Creare una classe
DateUtils
che dichiara solo metodi statici (e che quindi è inutile istanziare). Questa classe, dati due istanti in input, deve essere capace di restituire il numero di:• secondiche intercorrono tra i due istanti.
• minuti
• ore
• giorni
• settimane
• mesi
Soluzione
Il listato potrebbe essere il seguente:
import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; public class DateUtils { public static long getIntervallo(LocalDateTime ldt1, LocalDateTime ldt2, ChronoUnit chronoUnit) { return chronoUnit.between(ldt1, ldt2); } }
Ma ci sono tante alternative a questa soluzione. -
Esercizio I.c)
Modificare la classe
DateUtils
per dotarla di un metodo che, dato un istante in input, deve essere capace di restituire il numero di:• secondiche intercorrono tra l'istante specificato e l'istante attuale.
• minuti
• ore
• giorni
• settimane
• mesi
Soluzione
Il listato del metodo richiesto è semplice:
public static long getTempoPassato(Instant instant1, ChronoUnit chronoUnit) { return getIntervallo(instant1, Instant.now(), chronoUnit); }
Infatti abbiamo riutilizzato il metodo scritto nell'esercizio precedente. -
Esercizio I.d)
Nella classe
DateUtils
creare un metodo che restituisca l'ora esatta nel formato "HH:mm ss".
Soluzione
Il listato del metodo richiesto è semplice:
public static String oraEsatta() { LocalTime ora = LocalTime.now(); String oraEsatta = (ora.getHour() + ":" + ora.getMinute() + " " + ora.getSecond()); return oraEsatta; }
-
Esercizio I.e)
Nella classe
DateUtils
creare un metodo che formatti la data specificata secondo il pattern specificato in input.
Soluzione
Il listato del metodo richiesto è semplice:
public static String formattaData(LocalDateTime localDateTime, String pattern) throws DateTimeException { String formattedDate = null; try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); formattedDate = formatter.format(localDateTime); } catch (DateTimeException dateTimeException) { dateTimeException.printStackTrace(); throw dateTimeException; } return formattedDate; }
Si noti che abbiamo gestito l'eccezione solo per stamparne lo stack trace, per poi rilanciarla. Abbiamo anche inserito la clausolathrows
accanto alla definizione del metodo. In questo modo chi userà questo metodo saprà che potrebbe lanciare l'eccezione nel caso si specifichi un pattern errato. -
Esercizio I.f)
Nella classe
DateUtils
creare un metodo che analizzi la data specificata secondo il pattern specificato in input, e restituisca unaLocalDate
.
Soluzione
Il listato del metodo richiesto potrebbe essere il seguente:
public static LocalDate analizzaData(String data, String pattern) throws DateTimeParseException { LocalDate localDate = null; try { localDate = LocalDate.parse(data, DateTimeFormatter.ofPattern(pattern)); } catch (DateTimeParseException dateTimeParseException) { dateTimeParseException.printStackTrace(); throw dateTimeParseException; } return localDate; }
Si noti che anche in questo caso abbiamo gestito l'eccezione solo per stamparne lo stack trace, per poi rilanciarla. Abbiamo anche inserito la clausola throws accanto alla definizione del metodo. In questo modo chi userà questo metodo saprà che potrebbe lanciare l'eccezione nel caso si specifichi un pattern errato. L'eccezione questa volta è però di tipoDateTimeParseException
.Anche in questo caso la soluzione è molto semplice. Si noti che è possibile anche usare altre classi e altri metodi per ottenere gli stessi risultati ottenuti negli ultimi quattro esercizi. La libreria per la gestione delle date e del tempo è davvero semplice e potente. -
Esercizio I.g)
Creare una classe
TestDateUtils
per testare la correttezza dei metodi diDateUtils
.
Soluzione
Il listato richiesto potrebbe essere il seguente:
import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; public class TestDateUtils { private static final String FORMATO_DATA = "MM/dd/yy hh:mm a"; public static void main(String args[]) { final String oraEsatta = DateUtils.oraEsatta(); System.out.println("Sono le: " + oraEsatta); Instant duemila = Instant.parse("2000-01-01T00:00:00.00Z"); Instant duemiladieci = Instant.parse("2010-01-01T00:00:00.00Z"); long intervalloInGiorni = DateUtils.getIntervallo( duemila, duemiladieci, ChronoUnit.DAYS); System.out.println("Dal primo gennaio 2000 al primo gennaio 2010 " + "sono passati " + intervalloInGiorni + " giorni"); final long tempoPassatoInMinuti = DateUtils.getTempoPassato(duemila, ChronoUnit.MINUTES); System.out.println("Dal primo gennaio 2010 ad oggi sono passati " + tempoPassatoInMinuti + " minuti"); LocalDateTime localDateTime = LocalDateTime.now(); final String dataFormattata = DateUtils.formattaData(localDateTime, FORMATO_DATA); System.out.println("Data formattata: " + dataFormattata); LocalDate localDate = DateUtils.analizzaData(dataFormattata, FORMATO_DATA); System.out.println(localDate); System.out.println("Lanciamo un'eccezione!"); localDate = DateUtils.analizzaData(dataFormattata, "ABC"); } }
-
Esercizio I.h)
Quali tra le seguenti affermazioni sono corrette riguardo la standardizzazione dei metodi della libreria Date And Time API?
1. I metodifrom
hanno un parametro sempre meno completo rispetto al tipo che devono ritornare.
2. I metodiof
ritornano istanze di oggetti sulla classe su cui sono invocati.
3. I metodiplus
eminus
restituiscono copie dei parametri in input.
4.with
,is
,to
eat
sono usati solo come prefissi, mai come nomi completi.
Soluzione
Tutte le affermazioni sono vere tranne la numero 1. Infatti i metodi
from
hanno un parametro sempre più completo rispetto al tipo che devono ritornare. -
Esercizio I.i)
Quali tra le seguenti affermazioni sono corrette riguardo la geolocalizzazione della libreria Date And Time API?
1. La classeZoneId
astrae il concetto di area geografica che condivide lo stesso orario, ed è solitamente individuata da una coppia del tipo "regione/città".
2. La classeZoneOffset
astrae il concetto di fuso orario.
3. La classeZoneDateTime
rappresenta la versione localizzata della classeLocaleDate
.
4. Il metodoatZone
della classeLocalDateTime
restituisce unaZoneDateTime
.
Soluzione
Tutte le affermazioni sono vere tranne la numero 3. Infatti la classe
ZoneDateTime
rappresenta la versione localizzata della classeLocaleDateTime
, non diLocalDate
. -
Esercizio I.j)
Quali tra le seguenti affermazioni sono corrette riguardo la gestione del codice legacy della libreria Date And Time API?
1. Il metodofrom
è dichiarato sia nella classeGregorianCalendar
sia nella classeDate
.
2. Il metodotoInstant
per ottenere un'istanza della classeInstant
, è dichiarato sia nella classeCalendar
sia nella classeDate
.
3. È possibile sostituire la classeDate
con la classeInstant
.
4. La classeGregorianCalendar
può essere sostituita a seconda dei casi con i tipiZonedDateTime
,LocalTime
oLocalDate
.
5. La classeTimeZone
può essere sostituita a seconda dei casi con i tipiZoneId
oZoneOffset
.
Soluzione
Tutte le affermazioni sono vere.