Bună! Astăzi vom atinge un subiect nou important: modelele de design . Care sunt aceste modele? Cred că trebuie să cunoașteți expresia „ nu reinventați roata ”. În programare, ca și în multe alte domenii, există un număr mare de situații comune. Pe măsură ce dezvoltarea software-ului a evoluat, au fost create soluții gata făcute care funcționează pentru fiecare dintre ele. Aceste soluții se numesc modele de design. Prin convenție, un model este o soluție formulată astfel: „dacă trebuie să faci X în programul tău, atunci acesta este cel mai bun mod de a face asta”. Există o mulțime de modele. Le este dedicată excelenta carte „Head First Design Patterns”, cu care cu siguranță ar trebui să vă familiarizați.
Pe scurt, un model constă dintr-o problemă comună și o soluție corespunzătoare care poate fi considerată un fel de standard. În lecția de astăzi, vom întâlni unul dintre aceste modele: Adaptor. Numele lui spune totul și ați întâlnit adaptoare de multe ori în viața reală. Unele dintre cele mai comune adaptoare sunt cititoarele de carduri pe care le au multe computere și laptop-uri.
Să presupunem că avem un fel de card de memorie. Deci care este problema? Nu știe cum să interacționeze cu computerul. Nu au o interfață comună. Computerul are un port USB, dar nu putem introduce cardul de memorie în el. Cardul nu poate fi conectat la computer, așa că nu ne putem salva fotografiile, videoclipurile și alte date. Un cititor de carduri este un adaptor care rezolvă această problemă. La urma urmei, are un cablu USB! Spre deosebire de cardul în sine, cititorul de carduri poate fi conectat la computer. Au o interfață comună cu computerul: USB. Să vedem cum arată asta în practică:
Un set de metode este exact ceea ce este o interfață. După cum puteți vedea, această clasă are un
Cu toate acestea, interfețele
Și deși obiectul nostru


public interface USB {
void connectWithUsbCable();
}
Aceasta este interfața noastră USB cu o singură metodă de conectare prin 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!");
}
}
Aceasta este clasa noastră care reprezintă cardul de memorie. Are deja cele 2 metode de care avem nevoie, dar iată problema: nu implementează interfața USB. Cardul nu poate fi introdus în portul 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();
}
}
Și iată adaptorul nostru! Ce faceCardReader
clasa face și ce anume îl face un adaptor? Totul este simplu. Clasa care se adaptează (MemoryCard) devine unul dintre câmpurile adaptorului. Are sens. Când punem un card de memorie în interiorul unui cititor de carduri în viața reală, acesta devine și o parte a acestuia. Spre deosebire de cardul de memorie, adaptorul partajează o interfață cu computerul. Are cablu USB, adică poate fi conectat la alte dispozitive prin USB. De aceea, clasa noastră CardReader implementează interfața USB. Dar ce se întâmplă exact în cadrul acestei metode? Exact ceea ce trebuie să se întâmple! Adaptorul deleagă munca pe cardul nostru de memorie. Într-adevăr, adaptorul nu face nimic de la sine. Un cititor de carduri nu are nicio funcționalitate independentă. Sarcina sa este doar de a conecta computerul și cardul de memorie pentru a permite cardului să-și facă treaba - copierea fișierelor!connectWithUsbCable()
metoda) pentru a satisface „nevoile” cardului de memorie. Să creăm un program client care va simula o persoană care dorește să copieze date de pe un card de memorie:
public class Main {
public static void main(String[] args) {
USB cardReader = new CardReader(new MemoryCard());
cardReader.connectWithUsbCable();
}
}
Deci ce am primit? Ieșire din consolă:
Memory card successfully inserted!
The data has been copied to the computer!
Excelent. Ne-am atins obiectivul! Iată un link către un videoclip cu informații despre modelul adaptorului:
Cursuri de abstracte pentru cititori și scriitori
Acum ne vom întoarce la activitatea noastră preferată: a afla despre câteva clase noi pentru lucrul cu input și output :) Mă întreb despre câte am învățat deja. Astăzi vom vorbi despre claseleReader
și Writer
. De ce anume acele clase? Pentru că sunt legate de secțiunea noastră anterioară despre adaptoare. Să le examinăm mai detaliat. Vom începe cu Reader
. Reader
este o clasă abstractă, deci nu vom putea crea obiecte în mod explicit. Dar de fapt ești deja familiarizat cu el! La urma urmei, ești bine familiarizat cu clasele BufferedReader
și InputStreamReader
, care sunt descendenții lui :)
public class BufferedReader extends Reader {
…
}
public class InputStreamReader extends Reader {
…
}
Clasa InputStreamReader
este un adaptor clasic. După cum probabil vă amintiți, putem transmite un InputStream
obiect constructorului său. Pentru a face acest lucru, folosim de obicei System.in
variabila:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Dar ce InputStreamReader
face? Ca orice adaptor, convertește o interfață la alta. În acest caz, InputStream
interfața cu Reader
interfața. Inițial, avem InputStream
clasa. Funcționează bine, dar îl puteți folosi doar pentru a citi octeți individuali. În plus, avem o Reader
clasă abstractă. Are unele funcționalități foarte utile — știe să citească caracterele! Cu siguranță avem nevoie de această abilitate. Dar aici ne confruntăm cu problema clasică rezolvată de obicei de adaptoare – interfețe incompatibile. Ce înseamnă asta? Să aruncăm o privire la documentația Oracle. Iată metodele clasei InputStream
. 
read()
metoda (câteva variante, de fapt), dar poate citi doar octeți: fie octeți individuali, fie mai mulți octeți folosind un buffer. Dar această opțiune nu ne convine – vrem să citim caractere. Avem nevoie de funcționalitatea care este deja implementată în Reader
clasa abstractă . Putem vedea acest lucru și în documentație. 
InputStream
și Reader
sunt incompatibile! După cum puteți vedea, fiecare implementare a read()
metodei are parametri și valori returnate diferiți. Și aici avem nevoie InputStreamReader
! Va acționa ca un adaptor între clasele noastre. La fel ca în exemplul cu cititorul de carduri, pe care l-am considerat mai sus, punem o instanță a clasei care se adaptează „în interiorul” clasei adaptor, adică trecem una constructorului acesteia. În exemplul anterior, punem un MemoryCard
obiect în interiorul CardReader
. Acum transmitem un InputStream
obiect constructorului InputStreamReader
! Folosim System.in
variabila noastră familiară ca InputStream
:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Și într-adevăr, uitându-ne la documentația pentru InputStreamReader
, putem observa că adaptarea a reușit :) Acum avem la dispoziție metode de citire a personajelor. 
System.in
(fluxul legat de tastatură) nu a permis inițial acest lucru, creatorii limbii au rezolvat această problemă prin implementarea modelului adaptorului. Clasa Reader
abstractă, ca majoritatea claselor I/O, are un frate geamăn — Writer
. Are același mare avantaj ca Reader
- oferă o interfață convenabilă pentru lucrul cu personajele. În cazul fluxurilor de ieșire, problema și soluția ei arată la fel ca și în cazul fluxurilor de intrare. Există o OutputStream
clasă care poate scrie doar octeți, există oWriter
clasă abstractă care știe să lucreze cu personaje și există două interfețe incompatibile. Această problemă este din nou rezolvată de modelul adaptorului. Folosim OutputStreamWriter
clasa pentru a adapta cu ușurință cele două interfețe ale claselor Writer
și una OutputStream
la alta. După transmiterea unui OutputStream
flux de octeți către constructor, putem folosi un OutputStreamWriter
pentru a scrie caractere mai degrabă decât octeți!
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();
}
}
Am scris caracterul cu codul 32144 (綐) în fișierul nostru, eliminând nevoia de a lucra cu octeți :) Asta e tot pentru astăzi. Ne vedem la următoarele lecții! :)
GO TO FULL VERSION