Oggetti persistenti = Oggetti che sono capaci a scriversi su file e rileggersi da file. Servono per "ricordare" lo stato di un oggetto tra una esecuzione e l'altra del programma.
Classi persistenti implementano l'interfaccia Serializable, che non ha metodi, ma occorre dichiarare che una classe implementa Serializable per poterla scrivere e rileggere attraverso i metodi di ObjectOutputStream e ObjectInputStream.
Classe le cui istanze rappresentano file e sono in grado di scrivere oggetti appartenenti a classi che implementano l'interfaccia Serializable.
Costruzione di un'istanza di ObjectOutputStream, per es. sia "pippo.txt" il nome del file, e scrittura di un oggetto sul file:
FileOutputStream fos = new FileOutputStream("pippo.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(...oggetto da scrivere...); oos.close(); fos.close();Nota: tutte queste operazioni possono sollevare eccezioni di classe IOException.
Classe le cui istanze rappresentano file e sono in grado di leggere oggetti appartenenti a classi che implementano l'interfaccia Serializable.
Costruzione di un'istanza di ObjectInputStream, per es. sia "pippo.txt" il nome del file, e lettura di un oggetto dal file:
FileInStream fis = new FileInputStream("pippo.txt"); ObjectInputStream ois = new ObjectInputStream(fis); ...oggetto da leggere... = (...classe dell'oggetto...)ois.readObject(); ois.close(); fis.close();Note:
Il magazzino tiene traccia degli articoli presenti. Ogni articolo ha un codice numerico che lo identifica all'interno del magazzino. Ha inoltre una una stringa di descrizione. Ogni articolo e' presente in magazzino con un certo numero di pezzi.
Il magazzino puo' fare le seguenti operazioni:
File Articolo.java.
Implementa l'interfaccia Serializable, quindi i suoi oggetti possono essere scritti e riletti da file.
Ha due attributi: descrizione (stringa) e numero di pezzi (intero).
Ha i seguenti metodi pubblici:
All'interno del magazzino (la prossima classe che vedremo) gli articoli sono tenuti in un array. I codici degli articoli sono numeri interi a partire da zero. Ogni articolo e' memorizzato nella posizione dell'array corrispondente al suo codice. L'array ha un certo numero di posti (capacita' del magazzino), e in generale non tutti sono occupati. Quelli non occupati contengono l'oggetto nullo (null).
Quando registro un nuovo articolo, cerco una posizione vuota nell'array e lo memorizzo in quella posizione, che diventa il codice dell'articolo.
Quando depenno un articolo, lo calcello dall'array mettendo al suo posto l'oggetto nullo. La posizione che occupava nell'array rimane libera. Di conseguenza, lo stesso codice potra' poi essere usato per un nuovo articolo che sara' registrato in futuro.
File Magazzino.java.
Implementa l'interfaccia Serializable, quindi i suoi oggetti possono essere scritti e riletti da file.
Attributi (privati):
Costruttori:
Metodi che leggono la situazione del magazzino:
Metodi che modificano la situazione del magazzino:
File GestioneMagazzino.java. Interfaccia grafica per Magazzino. Puo' funzionare sia come applet (e' sottoclasse di Applet) che come applicazione (ha un main che mette l'applet in un frame).
Ha tra i suoi attributi un oggetto magazzino di classe Magazzino.
Come applicazione, puo' essere eseguita senza parametri
(in questo caso crea un nuovo magazzino vuoto) oppure con
parametro il nome di un file da cui leggere il magazzino.
Tra gli attributi ha anche il nome fileName
del file contenente il magazzino.
L'applet contiene quattro bottoni:
Ogni bottone fa apparire una finestra di dialogo.
Un'altra finestra di dialogo appare per segnalare errori
(le varie eccezioni che possono sorgere).
Gli altri attributi sono i quattro bottoni (di classe Button) e le cinque finestre di dialogo (di sotto-classi di Dialog appositamente definite, ved. dopo).
Il metodo init
costruisce le 5 finestre di dialogo ed associa a ciascuna i listener
previsti a seconda della classe di appartenenza.
Costruisce i 4 bottoni e li aggiunge all'applet.
Lo vedremo piu' in dettaglio
dopo.
Le varie finestre di dialogo sono realizzate con classi a parte.
Consideriamo gli elementi che queste finestre devono presentare:
Considerando il contenuto delle finestre nell'ordine in cui sono state descritte, notiamo che ognuna aggiunge elementi alla precedente, quindi sembra ragionevole organizzare le rispettive classi un una gerarchia cosi' fatta:
Definisce l'organizzazione comune a tutte le finestre di dialogo.
Usata direttamente per mostrare i messaggi di errore.
Usata anche indirettamente come superclasse di Dialog2.
La costruzione del contenuto della finestra e' fatta nel metodo "costruisci" che viene poi chiamato nel costruttore prima di eseguire "pack".
La finestra e' strutturata con BorderLayout. La posizione sud e' occupata da un pannello contenente finora solo un bottone (Ok). La posizione centrale inizialmente contiene nulla ma puo' essere riempita da un pannello chiamando la funzione "addCenter".
La finestra per errori riempie la parte centrale con un pannello contenente un'etichetta col messaggio. Questo pannello viene costruito e aggiunto al volo subito prima di mostrare la finestra (ved. metodi prepareErrorDialog della classe GestioneMagazzino).
Le sotto-classi aggiungeranno altri bottoni alla parte in basso e riempiranno la parte centrale con pannelli costruiti diversamente.
La funzione "addOkListener" permette di associare una callback al bottone "Ok". La funzione "clear" qui non fa nulla, ma sara' re-implementata nelle sotto-classi.
Sotto-classe di Dialog1.
Usata direttamente per inserire un nuovo articolo.
Usata anche indirettamente come superclasse di Dialog3.
Rispetto alla classe Dialog1, aggiunge al pannello in posizione sud altri due bottoni (Clear e Dismiss). Inoltre riempie la posizione centrale con un pannello contenente un'etichetta affiancata a un campo di testo.
Tale pannello centrale e' organizzato a griglia (1 riga e 2 colonne)
e predisposto in modo tale che possa essere espanso con nuove righe nelle
sotto-classi. Infatti il numero di righe non e' fisso ma
e' dato dal risultato
della funzione "righe" che sara' ridefinita nelle sotto-classi.
Anche la stringa da mostrare nell'etichetta esplicativa
non e' fissa ma e' data dal risultato della funzione "dicitura"
che sara' ridefinita nelle sotto-classi.
La funzione "clear", ereditata da Dialog1, e' re-implementata per cancellare il campo di testo.
Le funzioni "addClearListener" e "addDismissListener" permettono di associare callback ai bottoni "Clear" e "Dismiss". Inoltre la funzione "addInfoListener" permette di associare una callback al campo di testo, che scatta quando l'utente batte "return". La funzione "scritto1" restituisce il testo scritto nel campo.
Sotto-classe di Dialog2.
Usata direttamente per scaricare un articolo.
Usata anche indirettamente come superclasse di Dialog4.
Rispetto alla classe Dialog2, l'etichetta esplicativa a fianco del campo di testo cambia (avendo re-implementato la funzione "dicitura") in quanto il campo di testo e' destinato a contenere il codice di un articolo e non piu' la descrizione.
Il pannello in centro ha due righe in piu' (essendo stata re-implementata la funzione "righe"), e contiene due etichette destinate a mostrare descrizione e numero di pezzi dell'articolo corrispondente al codice inserito, ciascuna con accanto la sua etichetta esplicativa.
La funzione "clear" e' re-implementata per pulire anche le due nuove etichette (quelle per descrizione e numero pezzi). La nuova funzione "info" scrive due stringe assegnate nelle due etichette sopra citate.
Sotto-classe di Dialog3.
Usata per le finestre di movimento entrata e uscita.
Rispetto alla classe Dialog3,
il pannello al centro ha una riga in piu'
(re-implementata la funzione "righe") con
un altro campo di testo affiancato dalla relativa etichetta.
Nel nuovo campo di testo l'utente scrive il numero di pezzi da
far entrare / uscire.
La funzione "clear" e' re-implementata per azzerare anche questo
nuovo campo di testo.
La nuova funzione "scritto2" restituisce la stringa presente nel
nuovo campo di testo.
Una serie di classi interne a GestioneMagazzino implementa gli event listener che definiscono i comportamenti per le varie operazioni.
Lo schema generale del comportamento e' questo:
Sostituisce costruttore e main nel caso in cui il programma gira come applet.
Costruisce tutte le finestre di dialogo (modali), costruisce i bottoni dell'applet e stabilisce le callback dei bottoni. Vediamo piu' in dettaglio...
Crea finestra di dialogo per gli errori (errorD) di classe Dialog1. Stabilisce comportamento per il bottone "Ok" (nascondera' la finestra, action listener di classe HideListener costruito passando la finestra da nascondere - qui errorD - come argomento).
Crea finestra di dialogo per nuovo articolo (nuovoD) di classe Dialog2. Stabilisce comportamento per i bottoni "Ok" (aggiunge l'articolo, action listener di classe EseguiNuovo), "Clear" (action listener di classe ClearListener) e "Dismiss" (action listener di classe HideListener). Gli ultimi due listener costruiti passando la finestra su cui agire - qui nuovoD - come argomento).
Crea finestra di dialogo per movimento entrata (entraD) di classe Dialog4. Stabilisce comportamento per i bottoni "Ok" (fa entrare pezzi, action listener di classe EseguiEntrata), "Clear" e "Dismiss" (come sopra), e per la battitura di "return" nel campo di testo (scrive nelle etichette le informazioni dell'articolo di codice dato, action listener di classe InfoListener).
Crea finestra di dialogo per movimento uscita (esciD) di classe Dialog4. Stabilisce comportamento per i bottoni "Ok" (fa uscire pezzi, action listener di classe EseguiUscita), "Clear" e "Dismiss", e battitura di "return" nel campo di testo (come sopra).
Crea finestra di dialogo per scarica articolo (scaricaD) di classe Dialog3. Stabilisce comportamento per i bottoni "Ok" (depenna articolo, action listener di classe EseguiScarica), "Clear" e "Dismiss", e battitura di "return" nel campo di testo (come sopra).
Poiche' una finestra di dialogo deve dipendere da un frame, la funzione getFrame cerca, risalendo nella gerarchia di contenimento delle finestre, il frame che contiene l'applet (un frame che lo contiene esiste anche quando gira nella pagina web...).
Stabilisce che l'applet e' gestito con FlowLayout, crea e aggiunge i quattro bottoni e ne stabilisce il comportamento: mostreranno le relative finestre di dialogo per "nuovo articolo", "movimento entrata", "movimento uscita" e "scarica articolo" (action listener di classe ShowListener costruito passando in argomento la finestra di dialogo da mostrare).
Se il programma funziona come applicazione, entrano in gioco i costruttori (che abbiamo visto prima) e il main.
Il metodo main crea un frame dove mettere l'applet, aggiunge la gestione della chiusura del frame, mostra il frame.