This shows you the differences between two versions of the page.
|
poo:breviare:breviar-13 [2021/01/17 19:34] carmen.odubasteanu [Builder] |
poo:breviare:breviar-13 [2026/01/12 08:32] (current) george.tudor1906 [1. Fluxuri (Streams)] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Breviar laborator 13 ====== | ||
| + | ===== Breviar 13 ===== | ||
| - | ===== Design Patterns ===== | + | ==== 1. Fluxuri (Streams) ==== |
| + | === 1.1 Introducere === | ||
| - | ==== Prezentare generala ==== | + | Dennis Ritchie implementează în 1984 primul sistem I/O pe bază de stream în cadrul |
| + | sistemului de operare Unix. Acest concept are la bază crearea unui canal de comunicație | ||
| + | între două entități: **sursă** și **destinație**. Sursa scrie informații în canalul de | ||
| + | comunicație, iar destinația poate să citească aceste date, canalul permițând trecerea | ||
| + | fluxului de date într-o singură direcție. | ||
| + | **Clasificarea fluxurilor:** | ||
| - | Un șablon de proiectare descrie o problema care se întâlnește în mod repetat în proiectarea programelor și soluția generala pentru problema respectiva, astfel încât sa poata fi utilizata oricând, dar nu în același mod de fiecare data. Soluția este exprimata folosind claseși obiecte. | + | După direcția canalului de comunicație: |
| - | Atât descrierea problemei cât și a soluției sunt abstracte astfel încât sa poata fi folosite în multe situații diferite. | + | * fluxuri de intrare (pentru citirea datelor) |
| - | Scopul șabloanelor de proiectare este de a asista rezolvarea unor probleme similare cu unele deja întâlniteși rezolvate anterior. Ele ajuta la crearea unui limbaj comun pentru comunicarea experienței despre aceste problem și soluțiile lor. | + | * fluxuri de ieșire (pentru scrierea datelor) |
| - | Cele 4 elemente cheie care definesc un șablon de proiectare sunt urmatoarele: | + | După tipul de date pe care le operează: |
| + | * fluxuri de octeți (transfer serial pe 8 biți) | ||
| + | * fluxuri de caractere (transfer serial pe 16 biți) | ||
| - | 1. Numele șablonului de proiectare – având în vedere ca exista multe tipuri de șabloane de proiectare, este important ca fiecaruia sa îi fie aplicat un nume sugestiv, în strânsa legatura cu problema pe care o rezolva, care sa permita identificarea rapida a acestuia și a documentației aferente. | + | După acțiunea lor: |
| + | * fluxuri primare de citire/scriere a datelor | ||
| + | * fluxuri pentru procesarea datelor | ||
| - | 2. Descrierea situatiei în care poate fi aplicat – este foarte important sa știm care sunt situațiile în care putem aplica un șablon de proiectare. De aceea, este nevoie sa se prezinte o descriere a problemei și a contextului în care ar putea sa apara. Aceasta descriere poate fi realizata din perspective diferite: | + | Datorită faptului că există două direcții de comunicare, există două tipuri mari de |
| + | stream-uri pentru orice nod de comunicație: **input stream** și **output stream**. | ||
| + | Tastatura ar fi un exemplu de input stream, iar monitorul un output stream. | ||
| + | Sursa și destinația nu trebuie să fie neapărat periferice, ele pot fi și module soft. | ||
| - | • Ar putea sa fie o descriere axata pe aspecte specifice de proiectare, cum ar fi modul de reprezentare a diverșilor algoritmi folosind principiile programarii orientate pe obiecte. | + | === 1.2 Fluxuri de octeți === |
| - | • Poate fi realizata o descriere care sa conțina o ierarhie de clase sau o structura de obiecte care sunt implicate în implementarea șablonului de proiectare. | + | Programele folosesc fluxuri de octeți pentru a citi sau scrie date pe 8 biți (un octet). |
| + | Fluxurile la nivel de octet utilizează două ierarhii având drept clase rădăcină: | ||
| + | **InputStream** și **OutputStream**. | ||
| - | • Uneori, este important sa specificam o lista de condiții care trebuie sa fie îndeplinite pentru a putea aplica șablonul de proiectare. În acest caz, descrierea trebuie sa cont,ina aceasta lista de condiții. | + | Cele mai utilizate clase sunt **FileInputStream** și **FileOutputStream**. |
| - | 3. Descrierea soluției – descrie elementele care alcatuiesc proiectarea, relațiile, responsabilitațile și colaborarile acestora. Este indicat ca soluția sa nu conțina doar codul complet, ci și o descriere formala a unei problem și modul în care o interacțiune generala a conceptelor (clase și obiecte) poate rezolva problema. | + | <code java> |
| + | public class Copy { | ||
| + | private String input, output; | ||
| - | 4. Rezultatele și consecințele utilizarii – reutilizarea codului reprezinta, adesea, un factor esențial în programarea orientata pe obiecte, motiv pentru care pentru un șablon de proiectare trebuie sa fie prezentate consecințele pe care le au folosirea acestuia asupra flexibilităț ii, extensibilitații sau portabilitații soluției software. | + | public Copy(String input, String output) { |
| + | this.input = input; | ||
| + | this.output = output; | ||
| + | } | ||
| - | ==== Builder ==== | + | public void copyFile() { |
| - | + | InputStream in = null; | |
| - | Acest pattern este folosit în restaurantele de tip fast food care furnizeaza meniul pentru copii. Un meniu pentru copii consta de obicei într-un fel principal, unul secundar, o bautura si o jucarie. Pot exista variatii în ceea ce privește conținutul mediului, dar procesul de creare este acelasi. Fie ca la felul principal se alege un hamburger sau un cheesburger procesul va fi același. Vânzatorul le va indica celor din spate ce sa puna pentru fiecare fel de mâncare, pentru bautura si jucarie. Toate acestea vor fi puse într-o punga si servite clientilor. | + | OutputStream out = null; |
| + | try { | ||
| + | in = new FileInputStream(input); | ||
| + | out = new FileOutputStream(output); | ||
| + | while (in.available() > 0) | ||
| + | out.write(in.read()); | ||
| + | } catch (FileNotFoundException e) { | ||
| + | e.printStackTrace(); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } finally { | ||
| + | try { | ||
| + | if (out != null) out.close(); | ||
| + | if (in != null) in.close(); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | <note warning> | ||
| + | IMPORTANT! Fișierele text trebuie să se afle în folderul rădăcină al proiectului | ||
| + | care conține clasele care prelucrează informația din aceste fișiere. | ||
| + | </note> | ||
| - | {{:poo:breviare:img.jpg?400|}} | + | === 1.3 Fluxuri de caractere === |
| - | + | Limbajul Java stochează valorile de tip caracter folosind convenții Unicode. | |
| - | Acest sablon realizeaza separarea constructiei de obiecte complexe de reprezentarea lor astfel încât acelasi proces sa poata crea diferite reprezentari. Builder-ul creeaza parti ale obiectului complex de fiecare data când este apelat si retine toate starile intermediare. Când departamentul este terminat, clientul primeste rezultatul de la builder. În acest mod, se obtine un control mai mare asupra procesului de constructie de noi obiecte. | + | Fluxurile de caractere I/O își translatează automat formatul intern de la setul |
| - | Spre deosebire de alte pattern-uri, din categoria creational, care creau produsele într-un singur pas, pattern-ul Builder construieste un produs pas cu pas la comanda coordonatorului. În cadrul acestei aplicatii, pattern-ul este folosit pentru a instantia departamentele. Pentru a întelege cum ar trebui folosit acest pattern, puteti urmari exemplul de mai jos. | + | de caractere locale. |
| + | |||
| + | Toate clasele de fluxuri de caractere moștenesc clasele **Reader** și **Writer**. | ||
| + | Pentru operațiile I/O cu fișiere: **FileReader** și **FileWriter**. | ||
| <code java> | <code java> | ||
| - | public class User { | + | public class Read { |
| - | private final String firstName; // required | + | private String input; |
| - | private final String lastName; // required | + | |
| - | private final int age; // optional | + | public Read(String input) { |
| - | private final String phone; // optional | + | this.input = input; |
| - | private final String address; // optional | + | |
| - | private User(UserBuilder builder) { | + | |
| - | this.firstName = builder.firstName; | + | |
| - | this.lastName = builder.lastName; | + | |
| - | this.age = builder.age; | + | |
| - | this.phone = builder.phone; | + | |
| - | this.address = builder.address; | + | |
| } | } | ||
| - | public String getFirstName() { | + | |
| - | return firstName; | + | public void read() { |
| + | FileReader stream = null; | ||
| + | BufferedReader br = null; | ||
| + | try { | ||
| + | stream = new FileReader(input); | ||
| + | br = new BufferedReader(stream); | ||
| + | String line = br.readLine(); | ||
| + | while (line != null) { | ||
| + | System.out.println(line); | ||
| + | line = br.readLine(); | ||
| + | } | ||
| + | } catch (FileNotFoundException e) { | ||
| + | e.printStackTrace(); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } finally { | ||
| + | try { | ||
| + | if (stream != null) stream.close(); | ||
| + | if (br != null) br.close(); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| } | } | ||
| - | public String getLastName() { | + | } |
| - | return lastName; | + | </code> |
| - | } | + | |
| - | public int getAge() { | + | <note> |
| - | return age; | + | Observație: Obiectul BufferedReader creează un flux de intrare a caracterelor cu |
| - | } | + | stocare temporară (și posibilitate de citire a caracterelor sub formă de linii) |
| - | public String getPhone() { | + | din fluxul de intrare a caracterelor primit ca parametru. |
| - | return phone; | + | </note> |
| - | } | + | |
| - | public String getAddress() { | + | === 1.4 Fluxuri cu zone tampon (Buffer) === |
| - | return address; | + | |
| - | } | + | Pentru un flux I/O fără zone tampon, fiecare cerere de citire sau scriere este |
| - | public String toString() { | + | administrată direct de sistemul de operare. Aceasta face ca programul să fie mai |
| - | return "User:"+this.firstName+" "+this.lastName+" "+this.age+" "+this.phone+" " + this.address; | + | puțin eficient, deoarece fiecare cerere declanșează accesul la disc, activitate |
| - | } | + | în rețea sau alte operații care consumă timp. |
| - | public static class UserBuilder { | + | |
| - | private final String firstName; | + | Pentru a reduce timpul de procesare, platforma Java implementează fluxuri I/O cu |
| - | private final String lastName; | + | zone tampon: |
| - | private int age; | + | * Fluxurile de intrare cu zone tampon citesc datele dintr-o zonă de memorie cunoscută ca **buffer** |
| - | private String phone; | + | * API-ul de intrare este apelat numai când tamponul este gol |
| - | private String address; | + | * Similar, fluxurile de ieșire scriu datele în zona de tampon și API-ul de ieșire este apelat când tamponul este plin |
| - | public UserBuilder(String firstName, String lastName) { | + | |
| - | this.firstName = firstName; | + | Un program poate converti un flux fără zonă tampon într-un flux cu zonă tampon |
| - | this.lastName = lastName; | + | astfel: un obiect de tip flux fără zonă tampon este trecut ca argument unui |
| - | } | + | constructor pentru o clasă de tip flux cu zonă tampon. |
| - | public UserBuilder age(int age) { | + | |
| - | this.age = age; | + | === 1.5 Fluxuri standard === |
| - | return this; | + | |
| - | } | + | Limbajul Java pune la dispoziția utilizatorului trei fluxuri standard pentru |
| - | public UserBuilder phone(String phone) { | + | comunicare cu consola: |
| - | this.phone = phone; | + | |
| - | return this; | + | | **Flux** | **Descriere** | **Referință** | |
| - | } | + | | Standard Input | pentru citirea datelor | System.in (InputStream) | |
| - | + | | Standard Output | pentru afișarea datelor | System.out (PrintWriter) | | |
| - | public UserBuilder address(String address) { | + | | Standard Error | pentru afișarea erorilor | System.err (PrintWriter) | |
| - | this.address = address; | + | |
| - | return this; | + | <note> |
| - | } | + | Observație: Nu este necesară instanțierea acestor trei stream-uri, deoarece ele |
| - | public User build() { | + | se deschid automat înaintea execuției aplicației. De asemenea, acestea nici nu |
| - | return new User(this); | + | se închid prin apelul metodei close(). |
| + | </note> | ||
| + | |||
| + | **Exemplu cu InputStreamReader și BufferedReader:** | ||
| + | |||
| + | <code java> | ||
| + | public class Suma { | ||
| + | int a, b; | ||
| + | |||
| + | public void read() { | ||
| + | InputStreamReader stream = null; | ||
| + | BufferedReader br = null; | ||
| + | try { | ||
| + | stream = new InputStreamReader(System.in); | ||
| + | br = new BufferedReader(stream); | ||
| + | String line = br.readLine(); | ||
| + | a = Integer.parseInt(line); | ||
| + | line = br.readLine(); | ||
| + | b = Integer.parseInt(line); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } finally { | ||
| + | try { | ||
| + | if (stream != null) stream.close(); | ||
| + | if (br != null) br.close(); | ||
| + | } catch (IOException e) { | ||
| + | e.printStackTrace(); | ||
| + | } | ||
| } | } | ||
| } | } | ||
| - | | ||
| - | public static void main(String[] args) { | ||
| - | User user1 = new User.UserBuilder("Lokesh", "Gupta") | ||
| - | .age(30) | ||
| - | .phone("1234567") | ||
| - | .address("Fake address 1234") | ||
| - | .build(); | ||
| - | User user2 = new User.UserBuilder("Jack", "Reacher") | ||
| - | .age(40) | ||
| - | .phone("5655") | ||
| - | //no address | ||
| - | .build(); | ||
| - | } | ||
| } | } | ||
| </code> | </code> | ||
| + | |||
| + | **Exemplu cu Scanner:** | ||
| + | |||
| + | <code java> | ||
| + | public class Test { | ||
| + | public static void main(String args[]) throws Exception { | ||
| + | Scanner in = new Scanner(System.in); | ||
| + | int a, b; | ||
| + | a = in.nextInt(); | ||
| + | b = in.nextInt(); | ||
| + | int result = a + b; | ||
| + | System.out.println(a + " + " + b + " = " + result); | ||
| + | in.close(); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==== 2. Funcționale ==== | ||
| + | |||
| + | === 2.1 Introducere === | ||
| + | |||
| + | În paradigma funcțională, funcțiile sunt valori de ordinul 1 ce pot fi manipulate | ||
| + | ca orice altă valoare. **Funcționalele** sunt funcții care manipulează alte funcții, | ||
| + | primindu-le ca argumente sau returnându-le ca rezultat. | ||
| + | |||
| + | === 2.2 Lista funcționalelor === | ||
| + | |||
| + | == foldl(function, init, list) == | ||
| + | |||
| + | Returnează rezultatul aplicării funcției **function** pe rând asupra unui element | ||
| + | din listă și a unui acumulator **init**. Ordinea folosirii elementelor din listă | ||
| + | este de la **stânga la dreapta**. | ||
| + | |||
| + | <code> | ||
| + | foldl(f(x, y) = x + y, 5, [0, 1, 2, 3]) | ||
| + | = f(f(f(f(5, 0), 1), 2), 3) | ||
| + | = f(f(f(5, 1), 2), 3) | ||
| + | = f(f(6, 2), 3) | ||
| + | = f(8, 3) | ||
| + | = 11 | ||
| + | </code> | ||
| + | |||
| + | == foldr(function, init, list) == | ||
| + | |||
| + | Are un comportament similar cu foldl, însă ordinea folosirii elementelor din listă | ||
| + | este de la **dreapta la stânga**. | ||
| + | |||
| + | <code> | ||
| + | foldr(f(x, y) = y, 4, [0, 1, 2, 3]) | ||
| + | = f(0, f(1, f(2, f(3, 4)))) | ||
| + | = f(0, f(1, f(2, 4))) | ||
| + | = f(0, f(1, 4)) | ||
| + | = f(0, 4) | ||
| + | = 0 | ||
| + | </code> | ||
| + | |||
| + | == map(function, list) == | ||
| + | |||
| + | Returnează lista rezultatelor aplicării unei funcții **f** asupra fiecărui element | ||
| + | dintr-o listă. | ||
| + | |||
| + | <code> | ||
| + | map(f(x) = 2*x, [0, 1, 2, 3]) => [0, 2, 4, 6] | ||
| + | </code> | ||
| + | |||
| + | == filter(predicat, list) == | ||
| + | |||
| + | Returnează lista elementelor dintr-o listă care satisfac un predicat **p**. | ||
| + | Un predicat este o funcție care are un rezultat de tip Boolean. | ||
| + | |||
| + | <code> | ||
| + | filter(f(x) = x % 2 == 0, [0, 1, 2, 3]) => [0, 2] | ||
| + | </code> | ||
| + | |||
| + | == reduce(function, list) == | ||
| + | |||
| + | Aplică funcția pentru primele două elemente din listă, apoi pentru rezultatul | ||
| + | obținut anterior și următorul element și tot așa. | ||
| + | |||
| + | <code> | ||
| + | reduce(f(x, y) = x + y, [47, 11, 42, 13]) | ||
| + | = f(f(f(47, 11), 42), 13) | ||
| + | = f(f(58, 42), 13) | ||
| + | = f(100, 13) | ||
| + | = 113 | ||
| + | </code> | ||
| + | |||
| + | == all(predicat, list) == | ||
| + | |||
| + | Primește un predicat și verifică dacă **toate** elementele din listă satisfac predicatul. | ||
| + | |||
| + | <code> | ||
| + | all(f(x) = x > 0, [0, 1, 2, 3]) => False (0 nu e > 0) | ||
| + | all(f(x) = x >= 0, [0, 1, 2, 3]) => True | ||
| + | </code> | ||
| + | |||
| + | == any(predicat, list) == | ||
| + | |||
| + | Primește un predicat și verifică dacă există **cel puțin un element** în listă | ||
| + | care satisface predicatul. | ||
| + | |||
| + | <code> | ||
| + | any(f(x) = x < 0, [1, 2, 3, 4]) => False | ||
| + | any(f(x) = x % 2 == 0, [1, 2, 3]) => True (2 e par) | ||
| + | </code> | ||
| + | |||
| + | === 2.3 Rezumat funcționale === | ||
| + | |||
| + | | **Funcțională** | **Descriere** | **Exemplu** | | ||
| + | | **foldl** | Agregare stânga→dreapta cu acumulator | foldl(+, 0, [1,2,3]) = 6 | | ||
| + | | **foldr** | Agregare dreapta→stânga cu acumulator | foldr(-, 0, [1,2,3]) = 2 | | ||
| + | | **map** | Transformă fiecare element | map(x→x*2, [1,2]) = [2,4] | | ||
| + | | **filter** | Păstrează elementele care satisfac predicatul | filter(par, [1,2,3]) = [2] | | ||
| + | | **reduce** | Agregare fără acumulator inițial | reduce(+, [1,2,3]) = 6 | | ||
| + | | **all** | Toate satisfac predicatul? | all(>0, [1,2]) = True | | ||
| + | | **any** | Există cel puțin unul care satisface? | any(<0, [1,2]) = False | | ||
| + | |||