CIAO! Oggi toccheremo un nuovo importante argomento: i design pattern . Quali sono questi modelli? Penso che tu debba conoscere l'espressione " non reinventare la ruota ". Nella programmazione, come in molti altri ambiti, ci sono un gran numero di situazioni comuni. Man mano che lo sviluppo del software si è evoluto, sono state create soluzioni già pronte che funzionano per ciascuno di essi. Queste soluzioni sono chiamate modelli di progettazione. Per convenzione, un modello è una soluzione formulata in questo modo: "se hai bisogno di fare X nel tuo programma, allora questo è il modo migliore per farlo". Ci sono molti modelli. A loro è dedicato l'ottimo libro "Head First Design Patterns", con cui dovreste assolutamente familiarizzare. In poche parole, un modello consiste in un problema comune e una soluzione corrispondente che può essere considerata una sorta di standard. Nella lezione di oggi incontreremo uno di questi schemi: Adapter. Il nome dice tutto e hai incontrato gli adattatori molte volte nella vita reale. Alcuni degli adattatori più comuni sono i lettori di schede che hanno molti computer e laptop. Supponiamo di avere una sorta di scheda di memoria. Allora, qual'è il problema? Non sa come interagire con il computer. Non condividono un'interfaccia comune. Il computer ha una porta USB, ma non possiamo inserire la scheda di memoria al suo interno. La scheda non può essere collegata al computer, quindi non possiamo salvare foto, video e altri dati. Un lettore di schede è un adattatore che risolve questo problema. Dopotutto, ha un cavo USB! A differenza della scheda stessa, il lettore di schede può essere collegato al computer. Condividono un'interfaccia comune con il computer: USB. Vediamo come appare in pratica:
public interface USB {
void connectWithUsbCable();
}
Questa è la nostra interfaccia USB con un solo metodo per la connessione tramite USB.
public class MemoryCard {
public void insert() {
System.out.println("Memory card successfully inserted!");
}
public void copyData() {
System.out.println("The data has been copied to the computer!");
}
}
Questa è la nostra classe che rappresenta la scheda di memoria. Ha già i 2 metodi di cui abbiamo bisogno, ma ecco il problema: non implementa l'interfaccia USB. La scheda non può essere inserita nella porta USB.
public class CardReader implements USB {
private MemoryCard memoryCard;
public CardReader(MemoryCard memoryCard) {
this.memoryCard = memoryCard;
}
@Override
public void connectWithUsbCable() {
this.memoryCard.insert();
this.memoryCard.copyData();
}
}
Ed ecco il nostro adattatore! Cosa fa ilCardReader
class e cosa lo rende esattamente un adattatore? È tutto semplice. La classe che si sta adattando (MemoryCard) diventa uno dei campi dell'adattatore. Questo ha senso. Quando inseriamo una scheda di memoria all'interno di un lettore di schede nella vita reale, anche questa ne diventa parte. A differenza della scheda di memoria, l'adattatore condivide un'interfaccia con il computer. Ha un cavo USB, cioè può essere collegato ad altri dispositivi tramite USB. Ecco perché la nostra classe CardReader implementa l'interfaccia USB. Ma cosa succede esattamente all'interno di questo metodo? Esattamente quello di cui abbiamo bisogno per accadere! L'adattatore delega il lavoro alla nostra scheda di memoria. In effetti, l'adattatore non fa nulla da solo. Un lettore di schede non ha alcuna funzionalità indipendente. Il suo compito è solo quello di collegare il computer e la scheda di memoria per consentire alla scheda di svolgere il proprio lavoro: copiare i file!connectWithUsbCable()
metodo) per soddisfare le "esigenze" della scheda di memoria. Creiamo un programma client che simulerà una persona che vuole copiare i dati da una scheda di memoria:
public class Main {
public static void main(String[] args) {
USB cardReader = new CardReader(new MemoryCard());
cardReader.connectWithUsbCable();
}
}
Quindi cosa abbiamo ottenuto? Uscita console:
Memory card successfully inserted!
The data has been copied to the computer!
Eccellente. Abbiamo raggiunto il nostro obiettivo! Di seguito è riportato un collegamento a un video con informazioni sul modello Adapter:
Classi astratte Reader e Writer
Ora torneremo alla nostra attività preferita: conoscere un paio di nuove classi per lavorare con input e output :) Mi chiedo quante ne abbiamo già imparate. Oggi parleremo delle classiReader
e Writer
. Perché proprio quelle classi? Perché sono correlati alla nostra sezione precedente sugli adattatori. Esaminiamoli più in dettaglio. Inizieremo con Reader
. Reader
è una classe astratta, quindi non saremo in grado di creare oggetti in modo esplicito. Ma in realtà lo conosci già! Dopotutto, conosci bene le classi BufferedReader
e InputStreamReader
, che sono i suoi discendenti :)
public class BufferedReader extends Reader {
…
}
public class InputStreamReader extends Reader {
…
}
La InputStreamReader
classe è un adattatore classico. Come probabilmente ricorderai, possiamo passare un InputStream
oggetto al suo costruttore. Per fare questo, di solito usiamo la System.in
variabile:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Ma cosa InputStreamReader
fa? Come ogni adattatore, converte un'interfaccia in un'altra. In questo caso, l' InputStream
interfaccia all'interfaccia Reader
. Inizialmente, abbiamo la InputStream
classe. Funziona bene, ma puoi usarlo solo per leggere singoli byte. Inoltre, abbiamo una Reader
classe astratta. Ha alcune funzionalità molto utili: sa leggere i caratteri! Abbiamo certamente bisogno di questa capacità. Ma qui affrontiamo il classico problema solitamente risolto dagli adattatori: interfacce incompatibili. Che cosa significa? Diamo un'occhiata alla documentazione di Oracle. Ecco i metodi della InputStream
classe. Un insieme di metodi è esattamente ciò che è un'interfaccia. Come puoi vedere, questa classe ha aread()
metodo (poche varianti, in effetti), ma può solo leggere byte: singoli byte o più byte utilizzando un buffer. Ma questa opzione non è adatta a noi: vogliamo leggere i caratteri. Abbiamo bisogno della funzionalità che è già implementata nella Reader
classe astratta . Possiamo anche vedere questo nella documentazione. Tuttavia, le interfacce InputStream
e Reader
non sono compatibili! Come puoi vedere, ogni implementazione del read()
metodo ha parametri e valori restituiti diversi. Ed è qui che ci serve InputStreamReader
! Agirà da adattatore tra le nostre classi. Come nell'esempio con il lettore di schede, che abbiamo considerato sopra, mettiamo un'istanza della classe adattata "all'interno" della classe dell'adattatore, cioè ne passiamo una al suo costruttore. Nell'esempio precedente, inseriamo un MemoryCard
oggetto all'interno di CardReader
. Ora stiamo passando un InputStream
oggetto al InputStreamReader
costruttore! Usiamo la nostra System.in
variabile familiare come InputStream
:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
E in effetti, guardando la documentazione di InputStreamReader
, possiamo vedere che l'adattamento è riuscito :) Ora abbiamo a nostra disposizione metodi per leggere i caratteri. E sebbene il nostro System.in
oggetto (il flusso associato alla tastiera) inizialmente non lo consentisse, i creatori del linguaggio hanno risolto questo problema implementando il modello dell'adattatore. La Reader
classe astratta, come la maggior parte delle classi I/O, ha un fratello gemello — Writer
. Ha lo stesso grande vantaggio di Reader
: fornisce una comoda interfaccia per lavorare con i personaggi. Con i flussi di output, il problema e la sua soluzione hanno lo stesso aspetto dei flussi di input. C'è una OutputStream
classe che può scrivere solo byte, c'è aWriter
classe astratta che sa come lavorare con i caratteri e ci sono due interfacce incompatibili. Questo problema è ancora una volta risolto dal modello dell'adattatore. Usiamo la OutputStreamWriter
classe per adattare facilmente le due interfacce delle classi Writer
e l' OutputStream
una all'altra. Dopo aver passato un OutputStream
flusso di byte al costruttore, possiamo usare an OutputStreamWriter
per scrivere caratteri anziché byte!
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"));
streamWriter.write(32144);
streamWriter.close();
}
}
Abbiamo scritto il carattere con il codice 32144 (綐) nel nostro file, eliminando la necessità di lavorare con i byte :) Per oggi è tutto. Ci vediamo alle prossime lezioni! :)
GO TO FULL VERSION