Documente Academic
Documente Profesional
Documente Cultură
septembrie 2016
Specializarea Informatic
Tematica general:
Partea 1. Algoritmic i programare
1. Baze de date relaionale. Primele trei forme normale ale unei relaii.
2. Interogarea bazelor de date cu operatori din algebra relaional.
3. Interogarea bazelor de date relaionale cu SQL (Select).
1
Cuprins
2
1. Algoritmic i programare
1.1.1. Cutri
Datele se afl n memoria intern, ntr-un ir de articole. Vom cuta un articol dup un
cmp al acestuia pe care l vom considera cheie de cutare. n urma procesului de cutare va
rezulta poziia elementului cutat (dac acesta exist).
Notnd cu k1, k2, ...., kn cheile corespunztoare articolelor i cu a cheia pe care o cutm,
problema revine la a gsi (dac exist) poziia p cu proprietatea a = kp.
De obicei articolele sunt pstrate n ordinea cresctoare a cheilor, deci vom presupune c
Uneori este util s aflm nu numai dac exist un articol cu cheia dorit ci i s gsim n caz
contrar locul n care ar trebui inserat un nou articol avnd cheia specificat, astfel nct s se
pstreze ordinea existent.
Deci problema cutrii are urmtoarea specificare:
O prim metod este cutarea secvenial, n care sunt examinate succesiv toate cheile.
Sunt deosebite trei cazuri: ak1, a>kn, respectiv k1 < a kn, cutarea avnd loc n al treilea caz.
3
sfdac
sfdac
sf-CautSecv
Se observ c prin aceast metod se vor executa n cel mai nefavorabil caz n-1 comparri,
ntruct contorul i va lua toate valorile de la 2 la n. Cele n chei mpart axa real n n+1 intervale.
Tot attea comparri se vor efectua n n-1 din cele n+1 intervale n care se poate afla cheia
cutat, deci complexitatea medie are acelai ordin de mrime ca i complexitatea n cel mai ru
caz.
Evident c n multe situaii acest algoritm face calcule inutile. Atunci cnd a fost deja
gsit cheia dorit este inutil a parcurge ciclul pentru celelalte valori ale lui i. Cu alte cuvinte
este posibil s nlocuim ciclul PENTRU cu un ciclu CTTIMP. Ajungem la un al doilea
algoritm, dat n continuare.
n cel mai ru caz i acest algoritm face acelai numr de operaii ca i subalgoritmul
Cautsecv. n medie numrul operaiilor este jumtate din numrul mediu de operaii efecuat de
subalgoritmul Cautsecv deci complexitatea este aceeai.
O alt metod, numit cutare binar, care este mult mai eficient, utilizeaz tehnica
"divide et impera" privitor la date. Se determin n ce relaie se afl cheia articolului aflat n
mijlocul coleciei cu cheia de cutare. n urma acestei verificri cutarea se continu doar ntr-o
jumtate a coleciei. n acest mod, prin njumtiri succesive se micoreaz volumul coleciei
rmase pentru cutare. Cutarea binar se poate realiza practic prin apelul funciei
BinarySearch(a, n, K, 1, n), descris mai jos, folosit n subalgoritmul dat n continuare.
4
Funcia BinarySearch(a, n, K, St, Dr) este:
Dac St Dr - 1
atunci BinarySearch := Dr
altfel m := (St+Dr) Div 2;
Dac a km
atunci BinarySearch := BinarySearch(a, n, K, St, m)
altfel BinarySearch := BinarySearch(a, n, K, m, Dr)
sfdac
sfdac
sf-BinarySearch
1.1.2. Sortri
Prin sortare intern vom nelege o rearanjare a unei colecii aflate n memoria intern
astfel nct cheile articolelor s fie ordonate cresctor (eventual descresctor).
Din punct de vedere al complexitii algoritmilor problema revine la ordonarea cheilor.
Deci specificarea problemei de sortare intern este urmtoarea:
5
1.1.2.1. Sortare prin selecie
Metoda "BubbleSort", compar dou cte dou elemente consecutive iar n cazul n care
acestea nu se afl n relaia dorit, ele vor fi interschimbate. Procesul de comparare se va ncheia
n momentul n care toate perechile de elemente consecutive sunt n relaia de ordine dorit.
Acest algoritm execut n cel mai nefavorabil caz (n-1)+(n-2)+ ... +2+1 = n(n-1)/2
comparri, deci complexitatea lui este O(n2).
O variant optimizat a algoritmului "BubbleSort" este :
6
Subalgoritmul BubbleSort(n, K) este:
Fie s := 0
Repet
Fie kod := 0; {Ipoteza "este ordine"}
Pentru i := 2; n-s execut
Dac ki-1 > ki atunci
t := ki-1;
ki-1 := ki;
ki := t;
kod := 1 {N-a fost ordine!}
sfdac
sfpentru
s := s + 1
pncnd kod = 0 sfrep {Ordonare}
sf-BubbleSort
1.1.2.3. Quicksort
Procedura QuickSort(n, K, St, Dr) va realiza ordonarea subirului kSt, kSt+1, ...,
kDr. Acest subir va fi rearanjat astfel nct kSt s ocupe poziia lui final (cnd irul este
ordonat). Dac i este aceast poziie, irul va fi rearanjat astfel nct urmtoarea condiie s fie
ndeplinit:
Odat realizat acest lucru, n continuare va trebui doar s ordonm subirul kSt, kSt+1,
... ,ki-1 prin apelul recursiv al procedurii QuickSort(n, K, St, i-1) i apoi subirul ki+1,
...,kDr prin apelul QuickSort(n, K, i+1, Dr). Desigur ordonarea acestor dou subiruri
(prin apelul recursiv al procedurii) mai este necesar doar dac acestea conin cel puin dou
elemente.
Procedura QuickSort este prezentat n continuare :
7
Subalgoritmul QuickSort (n, K, St, Dr) este:
Fie i := St; j := Dr; a := ki;
Repet
Cttimp kj a i (i < j) execut j := j - 1 sfct
ki := kj;
Cttimp ki a i (i < j) execut i := i + 1 sfct
kj := ki ;
pncnd i = j sfrep
Fie ki := a;
Dac St < i-1 atunci Cheam QuickSort(n, K, St, i - 1) sfdac
Dac i+1 < Dr atunci Cheam QuickSort(n, K, i + 1, Dr) sfdac
sf-QuickSort
Complexitatea algoritmului prezentat este O(n2) n cel mai nefavorabil caz, dar
complexitatea medie este de ordinul O(nlog2n).
8
x
1 1 2 3
x
2 1 2 3 1 2 3 1 2 3
x
3 123 123 123 123 123 123 123 123 123
9
Figura 1.2. Spaiul soluiilor posibile pentru generarea permutrilor
Pentru a finisa acest algoritm trebuie s precizm elementele nestandard prezente. Astfel,
avem nevoie de funcia boolean
condiii-continuare(k, posibil, v)
funcie care verific dac vectorul promitor posibil[1..k-1] completat cu valoarea v conduce la
un vector promitor.
Apoi, pentru a iniializa cutarea la nivelul j avem nevoie de a alege un element fictiv din
mulimea Sj, activitate realizat de funcia
10
init(j)
care returneaz acest element fictiv, care are rolul de a indica faptul c din mulimea S nc nu s-
a ales nici un element, deci dup el urmeaz primul element propriu din aceast mulime. Pentru
a cuta o valoare pe nivelul j, n ipoteza c valoarea curent nu e bun, avem nevoie de funcia
boolean
urmtor(j, v, nou)
care este True dac poate alege o valoare din Sj care urmeaz dup valoarea v, valoare notat
prin nou i False n cazul n care nu mai exist alte valori n Sj, deci nu mai poate fi fcut
alegerea. Cu aceste notaii algoritmul devine:
11
sfct
conditii-continuare:=kod
sf-conditii
12
S considerm urmtorul exemplu simplu referitor la prelucrarea vectorilor de numere
ntregi. S se scrie un modul referitor la prelucrarea unui vector cu elemente ntregi, cu
funcii corespunztoare pentru iniializarea vectorului, eliberarea zonei de memorie ocupate i
ridicarea la ptrat, respectiv afiarea elementelor vectorului. O posibilitate de implementare a
modulului este prezentat n fiierul vector1.cpp:
#include <iostream>
13
return 0;
}
Observm c dei n programul principal se lucreaz cu doi vectori nu putem s-i folosim
mpreun, deci de exemplu modulul vector1.cpp nu poate fi extins astfel nct s realizeze i
adunarea a doi vectori. n vederea nlturrii acestui neajuns s-au introdus tipurile abstracte
de date.
Tipurile abstracte de date realizeaz o legtur mai strns ntre datele problemei i operaiile
(funciile) care se refer la aceste date. Declararea unui tip abstract de date este asemntoare
cu declararea unei structuri, care n afar de date mai cuprinde i declararea sau definirea
funciilor referitoare la acestea.
De exemplu n cazul vectorilor cu elemente numere ntregi putem declara tipul abstract:
struct vect {
int* e;
int d;
void init(int* e1, int d1);
void distr() { delete [] e; }
void lapatrat();
void afiseaza();
};
Funciile declarate sau definite n interiorul structurii vor fi numite funcii membru iar datele
date membru. Dac o funcie membru este definit n interiorul structurii (ca i funcia distr
din exemplul de mai sus), atunci ea se consider funcie inline. Dac o funcie membru se
definete n afara structurii, atunci numele funciei se va nlocui cu numele tipului abstract
urmat de operatorul de rezoluie (::) i numele funciei membru. Astfel funciile init, lapatrat
i afiseaza vor fi definite n modul urmtor:
void vect::lapatrat()
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}
void vect::afiseaza()
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}
14
Dei prin metoda de mai sus s-a realizat o legtur ntre datele problemei i funciile
referitoare la aceste date, ele nu sunt protejate, deci pot fi accesate de orice funcie utilizator,
nu numai de funciile membru. Acest neajuns se poate nltura cu ajutorul claselor.
Un tip abstract de date clas se declar ca i o structur, dar cuvntul cheie struct se
nlocuiete cu class. Ca i n cazul structurilor referirea la tipul de dat clas se face cu
numele dup cuvntul cheie class (numele clasei). Protecia datelor se realizeaz cu
modificatorii de protecie: private, protected i public. Dup modificatorul de protecie se
pune caracterul :. Modificatorul private i protected reprezint date protejate, iar public date
neprotejate. Domeniul de valabilitate a modificatorilor de protecie este pn la urmtorul
modificator din interiorul clasei, modificatorul implicit fiind private. Menionm c i n
cazul structurilor putem s folosim modificatori de protecie, dar n acest caz modificatorul
implicit este public.
De exemplu clasa vector se poate declara n modul urmtor:
class vector {
int* e; //elementele vectorului
int d; //dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector() { delete [] e; }
void lapatrat();
void afiseaza();
};
Se observ c datele membru e i d au fost declarate ca date de tip private (protejate), iar
funciile membru au fost declarate publice (neprotejate). Bineneles, o parte din datele
membru pot fi declarate publice, i unele funcii membru pot fi declarate protejate, dac
natura problemei cere acest lucru. n general, datele membru protejate pot fi accesate numai
de funciile membru ale clasei respective i eventual de alte funcii numite funcii prietene
(sau funcii friend).
O alt observaie important referitoare la exemplul de mai sus este c iniializarea datelor
membru i eliberarea zonei de memorie ocupat s-a fcut prin funcii membru specifice.
Datele declarate cu ajutorul tipului de dat clas se numesc obiectele clasei, sau simplu
obiecte. Ele se declar n mod obinuit n forma:
nume_clas list_de_obiecte;
vector v;
15
vector(int* e1, int d1);
~vector() { delete [] e; }
este un destructor.
Tipurile abstracte de date de tip struct pot fi i ele considerate clase cu toate elementele
neprotejate. Constructorul de mai sus este declarat n interiorul clasei, dar nu este definit, iar
destructorul este definit n interiorul clasei. Rezult c destructorul este o funcie inline.
Definirea funciilor membru care sunt declarate, dar nu sunt definite n interiorul clasei se
face ca i n cazul tipurilor abstracte de date de tip struct, folosind operatorul de rezoluie.
Referirea la datele respectiv funciile membru ale claselor se face cu ajutorul operatorilor
punct (.) sau sgeat (->) ca i n cazul referirii la elementele unei structuri. De exemplu, dac
se declar:
vector v;
vector* p;
n interiorul funciilor membru ns referirea la datele respectiv funciile membru ale clasei se
face simplu prin numele acestora fr a fi nevoie de operatorul punct (.) sau sgeat (->). De
fapt compilatorul genereaz automat un pointer special, pointerul this, la fiecare apel de
funcie membru, i folosete acest pointer pentru identificarea datelor i funciilor membru.
Pointerul this va fi declarat automat ca pointer ctre obiectul curent. n cazul exemplului de
mai sus pointerul this este adresa vectorului v respectiv adresa referit de pointerul p.
Dac n interiorul corpului funciei membru afiseaza se utilizeaz de exemplu data membru d,
atunci ea este interpretat de ctre compilator ca i this->d.
Pointerul this poate fi folosit i n mod explicit de ctre programator, dac natura problemei
necesit acest lucru.
1.2.1.5. Constructorul
16
tipul parametrilor formali trebuie s fie diferit, altfel compilatorul nu poate s aleag
constructorul corespunztor.
Constructorul nu returneaz o valoare. n acest caz nu este permis nici folosirea cuvntului
cheie void.
Prezentm n continuare un exemplu de tip clasa cu mai muli constructori, avnd ca date
membru numele i prenumele unei persoane, i cu o funcie membru pentru afiarea numelui
complet.
Fiierul persoana.h:
class persoana {
char* nume;
char* prenume;
public:
persoana(); //constructor implicit
persoana(char* n, char* p); //constructor
persoana(const persoana& p1); //constructor de copiere
~persoana(); //destructor
void afiseaza();
};
Fiierul persoana.cpp:
#include <iostream>
#include <cstring>
#include "persoana.h"
persoana::persoana()
{
nume = new char[1];
*nume = 0;
prenume = new char[1];
*prenume = 0;
cout << "Apelarea constructorului implicit." << endl;
}
persoana::persoana(char* n, char* p)
{
nume = new char[strlen(n)+1];
prenume = new char[strlen(p)+1];
strcpy(nume, n);
strcpy(prenume, p);
cout << "Apelare constructor (nume, prenume).\n";
}
17
persoana::~persoana()
{
delete[] nume;
delete[] prenume;
}
void persoana::afiseaza()
{
cout << prenume << ' ' << nume << endl;
}
Fiierul persoanaTest.cpp:
#include "persoana.h"
int main() {
persoana A; //se apeleaza constructorul implicit
A.afiseaza();
persoana B("Stroustrup", "Bjarne");
B.afiseaza();
persoana *C = new persoana("Kernighan","Brian");
C->afiseaza();
delete C;
persoana D(B); //echivalent cu persoana D = B;
//se apeleaza constructorul de copire
D.afiseaza();
return 0;
}
class nume_clasa {
nume_clasa_1 ob_1;
nume_clasa_2 ob_2;
...
nume_clasa_n ob_n;
...
};
nume_clasa(lista_de_argumente):
ob_1(l_arg_1), ob_2(l_arg_2), ..., ob_n(l_arg_n)
18
unde lista_de_argumente respectiv l_arg_i reprezint lista parametrilor formali ai
constructorului clasei nume_clasa respectiv ai obiectului ob_i.
Dac clasa conine date membru de tip obiect atunci se vor apela mai nti constructorii
datelor membru, iar dup aceea corpul de instruciuni al constructorului clasei respective.
Fiierul pereche.cpp:
#include <iostream>
#include "persoana.h"
class pereche {
persoana sot;
persoana sotie;
public:
pereche() //definitia constructorului implicit
{ //se vor apela constructorii impliciti
} //pentru obiectele sot si sotie
pereche(persoana& sotul, persoana& sotia);
pereche(char* nume_sot, char* prenume_sot,
char* nume_sotie, char* prenume_sotie):
sot(nume_sot, prenume_sot),
sotie(nume_sotie, prenume_sotie)
{
}
void afiseaza();
};
void pereche::afiseaza()
{
cout << "Sot: ";
sot.afiseaza();
cout << "Sotie: ";
sotie.afiseaza();
}
int main() {
persoana A("Pop", "Ion");
persoana B("Popa", "Ioana");
pereche AB(A, B);
AB.afiseaza();
pereche CD("C","C","D","D");
CD.afiseaza();
pereche EF;
19
EF.afiseaza();
return 0;
}
Observm c n cazul celui de al doilea constructor, parametrii formali sot i sotie au fost
declarai ca i referine la tipul persoana. Dac ar fi fost declarai ca parametri formali de tip
persoana, atunci n cazul declaraiei:
constructorul de copiere s-ar fi apelat de patru ori. n astfel de situaii se creeaz mai nti
obiecte temporale folosind constructorul de copiere (dou apeluri n cazul de fa), dup care
se execut constructorii datelor membru de tip obiect (nc dou apeluri).
1.2.1.6. Destructorul
Destructorul este funcia membru care se apeleaz n cazul distrugerii obiectului. Destructorul
obiectelor globale se apeleaz automat la sfritul funciei main ca parte a funciei exit. Deci,
nu este indicat folosirea funciei exit ntr-un destructor, pentru c acest lucru duce la un ciclu
infinit. Destructorul obiectelor locale se execut automat la terminarea blocului n care s-au
definit. n cazul obiectelor alocate dinamic, de obicei destructorul se apeleaz indirect prin
operatorul delete (obiectul trebuie s fi fost creat cu operatorul new). Exist i un mod
explicit de apelare a destructorului, n acest caz numele destructorului trebuie precedat de
numele clasei i operatorul de rezoluie.
Numele destructorului ncepe cu caracterul ~ dup care urmeaz numele clasei. Ca i n cazul
constructorului, destructorul nu returneaz o valoare i nu este permis nici folosirea
cuvntului cheie void. Apelarea destructorului n diferite situaii este ilustrat de urmtorul
exemplu. Fiierul destruct.cpp:
#include <iostream>
#include <cstring>
scrie::scrie(char* n)
{
nume = new char[strlen(n)+1];
strcpy(nume, n);
cout << "Am creat obiectul: " << nume << '\n';
}
scrie::~scrie()
{
cout << "Am distrus obiectul: " << nume << '\n';
delete nume;
20
}
void functie()
{
cout << "Apelare functie" << '\n';
scrie local("Local");
}
scrie global("Global");
int main() {
scrie* dinamic = new scrie("Dinamic");
functie();
cout << "Se continua programul principal" << '\n';
delete dinamic;
return 0;
}
Prin folosirea tipurilor abstracte de date, se creeaz un tot unitar pentru gestionarea datelor i
a operaiilor referitoare la aceste date. Cu ajutorul tipului abstract clas se realizeaz i
protecia datelor, deci n general elementele protejate nu pot fi accesate dect de funciile
membru ale clasei respective. Aceast proprietate a obiectelor se numete ncapsulare
(encapsulation).
n viaa de zi cu zi ns ne ntlnim nu numai cu obiecte separate, dar i cu diferite legturi
ntre aceste obiecte, respectiv ntre clasele din care obiectele fac parte. Astfel se formeaz o
ierarhie de clase. Rezult a doua proprietate a obiectelor: motenirea (inheritance). Acest
lucru nseamn c se motenesc toate datele i funciile membru ale clasei de baz de ctre
clasa derivat, dar se pot aduga elemente noi (date membru i funcii membru) n clasa
derivat. n cazul n care o clas derivat are mai multe clase de baz se vorbete despre
motenire multipl.
O alt proprietate important a obiectelor care aparin clasei derivate este c funciile membru
motenite pot fi suprancrcate. Acest lucru nseamn c o operaie referitoare la obiectele
care aparin ierarhiei are un singur identificator, dar funciile care descriu aceast operaie pot
fi diferite. Deci, numele funciei i lista parametrilor formali este aceeai n clasa de baz i n
clasa derivat, dar descrierea funciilor difer ntre ele. Astfel, n clasa derivat funciile
membru pot fi specifice clasei respective, dei operaia se identific prin acelai nume.
Aceast proprietate se numete polimorfism.
21
class nume_clas_derivat : lista_claselor_de_baz {
//date membru noi i funcii membru noi
};
Observm c elementele de tip private ale clasei de baz sunt inaccesibile n clasa derivat.
Elementele de tip protected i public devin de tip protected, respectiv private dac
modificatorul de protecie referitor la clasa de baz este protected respectiv private, i rmn
neschimbate dac modificatorul de protecie referitor la clasa de baz este public. Din acest
motiv n general datele membru se declar de tip protected i modificatorul de protecie
referitor la clasa de baz este public. Astfel datele membru pot fi accesate, dar rmn
protejate i n clasa derivat.
22
1.3.3. Funcii virtuale
class baza {
public:
void functia_1();
void functia_2();
};
void baza::functia_1()
{
cout << "S-a apelat functia membru functia_1"
<< " a clasei de baza" << endl;
}
void baza::functia_2()
{
cout << "S-a apelat functia membru functia_2"
<< " a clasei de baza" << endl;
functia_1();
}
void derivata::functia_1()
{
cout << "S-a apelat functia membru functia_1"
<< " a clasei derivate" << endl;
}
int main() {
derivata D;
D.functia_2();
}
23
ns acest lucru nu este rezultatul dorit, deoarece n cadrul funciei main s-a apelat funcia
membru functia_2 motenit de la clasa de baz, dar funcia membru functia_1 apelat de
functia_2 s-a determinat nc n faza de compilare. n consecin, dei funcia membru
functia_1 s-a suprancrcat n clasa derivat nu s-a apelat funcia suprancrcat ci funcia
membru a clasei de baz.
Acest neajuns se poate nltura cu ajutorul introducerii noiunii de funcie membru virtual.
Dac funcia membru este virtual, atunci la orice apelare a ei, determinarea funciei membru
corespunztoare a ierarhiei de clase nu se va face la compilare ci la execuie, n funcie de
natura obiectului pentru care s-a fcut apelarea. Aceast proprietate se numete legare
dinamic, iar dac determinarea funciei membru se face la compilare, atunci se vorbete de
legare static.
Am vzut c dac se execut programul virtual1.cpp se apeleaz funciile membru
functia_1 i functia_2 ale clasei de baz. ns funcia membru functia_1 fiind suprancrcat
n clasa derivat, ar fi de dorit ca funcia suprancrcat s fie apelat n loc de cea a clasei de
baz.
Acest lucru se poate realiza declarnd functia_1 ca funcie membru virtual. Astfel, pentru
orice apelare a funciei membru functia_1, determinarea acelui exemplar al funciei membru
din ierarhia de clase care se va executa, se va face la execuie i nu la compilare. Ca urmare,
funcia membru functia_1 se determin prin legare dinamic.
n limbajul C++ o funcie membru se declar virtual n cadrul declarrii clasei respective n
modul urmtor: antetul funciei membru se va ncepe cu cuvntul cheie virtual.
Dac o funcie membru se declar virtual n clasa de baz, atunci suprancrcrile ei se vor
considera virtuale n toate clasele derivate ale ierarhiei.
n cazul exemplului de mai sus declararea clasei de baz se modific n felul urmtor.
class baza {
public:
virtual void functia_1();
void functia_2();
};
24
#include <iostream>
class fractie {
protected:
int numarator;
int numitor;
public:
fractie(int numarator1 = 0, int numitor1 = 1);
fractie produs(fractie& r); //calculeaza produsul a doua
//fractii, dar nu simplifica
fractie& inmulteste(fractie& r);
void afiseaza();
};
fractie fractie::produs(fractie& r)
{
return fractie(numarator * r.numarator, numitor * r.numitor);
}
fractie& fractie::inmulteste(fractie& q)
{
*this = this->produs(q);
return *this;
}
void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor;
else
cerr << "Fractie incorecta";
}
fractie fractie_scrie::produs(fractie& q)
{
fractie r = fractie(*this).produs(q);
cout << "(";
this->afiseaza();
cout << ") * (";
q.afiseaza();
cout << ") = ";
25
r.afiseaza();
cout << endl;
return r;
}
int main()
{
fractie p(3,4), q(5,2), r;
r = p.inmulteste(q);
p.afiseaza();
cout << endl;
r.afiseaza();
cout << endl;
fractie_scrie p1(3,4), q1(5,2);
fractie r1, r2;
r1 = p1.produs(q1);
r2 = p1.inmulteste(q1);
p1.afiseaza();
cout << endl;
r1.afiseaza();
cout << endl;
r2.afiseaza();
cout << endl;
return 0;
}
15 / 8
15 / 8
(3 / 4) * (5 / 2) = 15 / 8
15 / 8
15 / 8
15 / 8
Observm c rezultatul nu este cel dorit, deoarece operaia de nmulire s-a afiat numai o
singur dat, i anume pentru expresia r1 = p1.produs(q1). n cazul expresiei r2 =
p1.inmulteste(q1) ns nu s-a afiat operaia de nmulire. Acest lucru se datoreaz
faptului c funcia membru inmulteste nu s-a suprancrcat n clasa derivat. Deci s-a apelat
funcia motenit de la clasa fractie. n interiorul funciei inmulteste s-a apelat funcia
membru produs, dar deoarece aceast funcie membru s-a determinat nc n faza de
compilare, rezult c s-a apelat funcia referitoare la clasa fractie i nu cea referitoare la clasa
derivat fractie_scrie. Deci, afiarea operaiei s-a efectuat numai o singur dat.
Soluia este, ca i n exemplul anterior, declararea unei funcii membru virtuale, i anume
funcia produs se va declara ca funcie virtual. Deci declararea clasei de baz se modific n
felul urmtor:
class fractie {
protected:
int numarator;
int numitor;
public:
fractie(int numarator1 = 0, int numitor1 = 1);
virtual fractie produs(fractie& r); //calculeaza produsul a doua
//fractii, dar nu simplifica
fractie& inmulteste(fractie& r);
26
void afiseaza();
};
15 / 8
15 / 8
(3 / 4) * (5 / 2) = 15 / 8
(3 / 4) * (5 / 2) = 15 / 8
15 / 8
15 / 8
15 / 8
Deci, se observ c afiarea operaiei s-a fcut de dou ori, pentru ambele expresii. Funciile
virtuale, ca i alte funcii membru de fapt, nu trebuie neaprat suprancrcate n clasele
derivate. Dac nu sunt suprancrcate atunci se motenete funcia membru de la un nivel
superior.
Determinarea funciilor membru virtuale corespunztoare se face pe baza unor tabele
construite i gestionate n mod automat. Obiectele claselor care au funcii membru virtuale
conin i un pointer ctre tabela construit. De aceea gestionarea funciilor membru virtuale
necesit mai mult memorie i un timp de execuie mai ndelungat.
n cazul unei ierarhii de clase mai complicate, clasa de baz poate avea nite proprieti
generale despre care tim, dar nu le putem defini numai n clasele derivate. De exemplu s
considerm ierarhia de clase din Figura 1.3.
Observm c putem determina nite proprieti referitoare la clasele derivate. De exemplu
greutatea medie, durata medie de via i viteza medie de deplasare. Aceste proprieti se vor
descrie cu ajutorul unor funcii membru. n principiu i pentru clasa animal exist o greutate
medie, durat medie de via i vitez medie de deplasare. Dar aceste proprieti ar fi mult
mai greu de determinat i ele nici nu sunt importante pentru noi ntr-o generalitate de acest
fel. Totui pentru o tratare general ar fi bine, dac cele trei funcii membru ar fi declarate n
clasa de baz i definite n clasele derivate. n acest scop s-a introdus noiunea de funcie
membru virtual pur.
27
Funcia virtual pur este o funcie membru care este declarat, dar nu este definit n clasa
respectiv. Ea trebuie definit ntr-o clas derivat. Funcia membru virtual pur se declar
n modul urmtor. Antetul obinuit al funciei este precedat de cuvntul cheie virtual, i
antetul se termin cu = 0. Dup cum arat numele i declaraia ei, funcia membru virtual
pur este o funcie virtual, deci selectarea exemplarului funciei din ierarhia de clase se va
face n timpul execuiei programului.
Clasele care conin cel puin o funcie membru virtual pur se vor numi clase abstracte.
Deoarece clasele abstracte conin funcii membru care nu sunt definite, nu se pot crea obiecte
aparinnd claselor abstracte. Dac funcia virtual pur nu s-a definit n clasa derivat atunci
i clasa derivat va fi clas abstract i ca atare nu se pot defini obiecte aparinnd acelei
clase.
S considerm exemplul de mai sus i s scriem un program, care referitor la un porumbel,
urs sau cal determin dac el este gras sau slab, rapid sau ncet, respectiv tnr sau btrn.
Afiarea acestui rezultat se va face de ctre o funcie membru a clasei animal care nu se
suprancarc n clasele derivate. Fiierul abstract1.cpp:
#include <iostream>
class animal {
protected:
double greutate; // kg
double virsta; // ani
double viteza; // km / h
public:
animal( double g, double v1, double v2);
virtual double greutate_medie() = 0;
virtual double durata_de_viata_medie() = 0;
virtual double viteza_medie() = 0;
int gras() { return greutate > greutate_medie(); }
int rapid() { return viteza > viteza_medie(); }
int tanar()
{ return 2 * virsta < durata_de_viata_medie(); }
void afiseaza();
};
void animal::afiseaza()
{
cout << ( gras() ? "gras, " : "slab, " );
cout << ( tanar() ? "tanar, " : "batran, " );
cout << ( rapid() ? "rapid" : "incet" ) << endl;
28
double greutate_medie() { return 0.5; }
double durata_de_viata_medie() { return 6; }
double viteza_medie() { return 90; }
};
int main() {
porumbel p(0.6, 1, 80);
urs u(500, 40, 46);
cal c(900, 8, 70);
p.afiseaza();
u.afiseaza();
c.afiseaza();
return 0;
}
Observm c dei clasa animal este clas abstract, este util introducerea ei, pentru c multe
funcii membru pot fi definite n clasa de baz i motenite fr modificri n cele trei clase
derivate.
1.3.5. Interfee
n limbajul C++ nu s-a definit noiunea de interfa, care exist n limbajele Java sau C#. Dar
orice clas abstract, care conine numai funcii virtuale pure, se poate considera o interfa.
Bineneles, n acest caz nu se vor declara nici date membru n interiorul clasei. Clasa
abstract animal conine att date membru, ct i funcii membru nevirtuale, deci ea nu se
poate considera ca i un exemplu de interfa.
n continuare introducem o clas abstract Vehicul, care nu conine numai funcii membru
virtuale pure, i dou clase derivate din aceast clas abstract. Fiierul vehicul.cpp:
#include <iostream>
using namespace std;
class Vehicul
{
public:
virtual void Porneste() = 0;
29
virtual void Opreste() = 0;
virtual void Merge(int km) = 0;
virtual void Stationeaza(int min) = 0;
};
void Bicicleta::Porneste() {
cout << "Bicicleta porneste." << endl;
}
void Bicicleta::Opreste() {
cout << "Bicicleta se opreste." << endl;
}
void Bicicleta::Merge(int km) {
cout << "Bicicleta merge " << km <<
" kilometri." << endl;
}
void Bicicleta::Stationeaza(int min) {
cout << "Bicicleta stationeaza " << min <<
" minute." << endl;
}
void Masina::Porneste() {
cout << "Masina porneste." << endl;
}
void Masina::Opreste() {
cout << "Masina se opreste." << endl;
}
void Masina::Merge(int km) {
cout << "Masina merge " << km <<
" kilometri." << endl;
}
void Masina::Stationeaza(int min) {
cout << "Masina stationeaza " << min <<
" minute." << endl;
}
30
int main()
{
Vehicul *b = new Bicicleta;
Traseu(b);
Vehicul *m = new Masina;
Traseu(m);
delete m;
delete b;
}
n funcia main s-au declarat dou obiecte dinamice de tip Bicicleta, respectiv Masina, i n
acest fel, apelnd funcia Traseu obinem rezultate diferite, dei aceast funcie are ca
parametru formal numai un pointer ctre o clas abstract Vehicul.
31
modele. Pentru a exprima comportamentul dorit al sistemului, se folosesc alte elemente ca i
cazuri de utilizare, procese de afaceri (business processes), etc - dar acestea nu sunt discutate
n aceast seciune.
Primul model din Figura 1.4 prezint un extras din modelul conceptual pentru aplicaia
POS. Modelul este construit pentru a surprinde conceptele folosite de utilizatori. Aceste
concepte sunt folosite pentru a exprima comportamentul dorit, folosind alte elemente de
modelare.
PIM - Modele independente de platform (Platform Independent Models). Aceste
modele descriu cum funcioneaz sistemul, ntr-o manier independent de posibilele
platforme concrete n care va fi implementat. La acest nivel se introduc elemente
arhitecturale, iar diagramele de clase i de interaciuni ntre obiecte constituie dou
instrumente de baz pentru descrierea detaliat a sistemului (proiectare detaliat). Desigur,
se folosesc si alte elemente de modelare, structurale i comportamentale, dar acestea nu sunt
discutate aici, de exemplu - colaborri, maini cu stri, activiti, etc.
Al doilea model din Figura 1.4 prezint un extras din modelul PIM pentru POS. Att
modelele CIM ct i cele PIM conin doar construcii UML (ex. tipuri de date definite n
specificaia UML) i eventual extensii ale acestui limbaj (independente de platform).
32
exemplu tipuri de date specifice. Diagramele de clase i de interaciune ntre obiecte sunt de
asemenea folosite pentru aceste modele.
Ultimul model prezentat n Figura 1.4 reprezint o transformare a modelului PIM n
contextul implementrii sistemului n Java. Tipurile de date din acest model sunt tipuri Java
(ex. String, double), iar modelul include i un tip de date din pachetul java.sql.
Conform ghidului MDA, definim modele astfel nct n final s generm cod ctre
anumite platforme alese. Codul poate fi generat pornind de la modele PIM sau PSM. n
procesul de generare se folosesc corespondene ntre elementele modelelor i elementele
platformei alese.
1
OMG. Service Oriented Architecture Modeling Language, 2009. http://www.omg.org/spec/SoaML/
2
Open SOA. Service Component Architecture Specifications, 2007.
http://www.osoa.org/display/Main/Service+Component+Architecture+Specifications
3
IBM. IBM Rational Unified Process, 2007. http://www-01.ibm.com/software/awdtools/rup/
4
Beck, K. Test-Driven Development by Example, Addison Wesley, 2003.
5
Ambler, S.W. Agile Model Driven Development (AMDD): The Key to Scaling Agile Software Development,
2008. http://www.agilemodeling.com/essays/amdd.htm
33
modele executabile. Astzi, adoptarea standardului pentru modele executabile UML (fUML
- Foundational UML)6 este n curs de finalizare. Conform acestor procese, n viitorul apropiat
ne ateptm la adoptarea unui stil nou de dezvoltare n care vom construi doar modele i
vom scrie cod ntr-un limbaj textual7 definit pe elementele din aceste modele. Astfel,
modelele PSM i codul scris n limbaje ca i Java, C++ sau C# vor fi lsate n grija
instrumentelor CASE care le vor genera automat.
C. Corespondena dintre modele i cod
Corespondenele dintre modele i cod sunt importante dup cum relev punctele (A) i
(B). Dac generm cod din modele PIM, respectiv dac folosim un instrument CASE care
sincronizeaz modele PSM cu codul, e important sa tim ce elemente se vor genera din
modelele construite. Chiar dac lucrm agil i folosim "schie" de modele (fr a folosi
instrumente CASE), se pune aceeai problem.
innd cont i de modelele executabile amintite anterior (care sunt la nivel PIM), n
seciunile care urmeaz vom discuta numai corespondenele dintre modele PIM i
limbajele C++, Java i C#. Modelele PSM conin n plus fa de cele PIM i tipuri de date
specifice anumitor limbaje, astfel corespondenele dintre modele PSM i cod sunt aceleai,
doar c sunt prezente n modele i extensii UML conform tipurilor specifice.
Diagramele sunt reprezentri grafice (n general 2D) ale unor elemente dintr-un model.
Diagramele de clase reprezint tipurile de obiecte folosite n sistem i relaiile dintre acestea.
Elementele structurale selectate n aceast seciune sunt (a) tipuri de obiecte: clase,
interfee, enumerri; (b) gruparea elementelor folosind pachete i (c) relaii ntre aceste
elemente: asocieri, generalizri, realizri i dependene.
6
OMG. Semantics Of A Foundational Subset For Executable UML Models (FUML), 2010.
http://www.omg.org/spec/FUML/
7
OMG. Concrete Syntax For UML Action Language (Action Language For Foundational UML - ALF), 2010.
http://www.omg.org/spec/ALF/
34
Modelele conceptuale sunt de tip CIM i sunt folosite pentru a genera modele PIM. Ca
i modele CIM, ele pot s nu conin detalii privind reprezentarea atributelor. Dac procesul
de dezvoltare folosit nu presupune folosirea unui model CIM (ci PIM sau PSM), atunci
modelul din Figura 1.5 este un model PIM sau PSM incomplet.
n context PIM, arhitectura sistemului din perspectiv structural este descris folosind
pachete (organizate ierarhic) i dependene ntre acestea - a se vedea Figura 1.6 pentru POS.
Pachetele sunt definite cu responsabiliti coezive, de exemplu interaciunea cu utilizatorul
(ui), faad peste domeniu (service), entiti (model) i depozite de obiecte sau obiecte de
acces la date (repository, inmemory repository).
8
Robert C. Martin. Design Principles and Design Patterns, 2004.
http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
9
SOLID Design Principles: Single responsibility, Open-closed, Liskov substitution, Interface segregation and
Dependency inversion, http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
35
Figura 1.7 Arhitectur stratificat - justificare dependene
La nivel PIM sau PSM, diagramele de clase sunt folosite pentru a rafina entitile i
relaiile dintre acestea - a se vedea Figura 1.8. Aceste elemente vor fi discutate n
subseciunile care urmeaz.
A. Pachete
Pachetele UML [29, 30] grupeaz elemente din model i ofer un spaiu de nume
pentru elementele grupate. Din perspectiva elementelor discutate n acest document,
pachetele pot conine tipuri de date i alte pachete. Un tip de date sau un pachet poate fi parte
a unui singur pachet.
n termeni ai limbajelor de programare, pachetele UML corespund pachetelor Java i
spaiilor de nume C++ i C#. Pachetele UML sunt referite folosind operatorul de rezoluie ::,
36
la fel ca i in C++ i C#. De exemplu, numele complet al pachetului ui din Figura 1.6 sau
Figura 1.7 este pos::ui.
Diagramele de clase ce indic cu preponderen pachetele unui sistem sunt folosite
pentru a descrie arhitectura acestuia - a se vedea Figura 1.6 pentru sistemul POS.
Dependenele ntre pachete indic un sumar al dependenelor dintre elementele coninute i
elementele din alte pachete. Din perspectiv arhitectural, buna gestionare a acestor
dependene este crucial n procesul de construire i ntreinere a sistemului.
B. Clase
O clas UML [29, 30] reprezint o mulime de obiecte cu aceleai elemente structurale
(proprieti) i comportamentale (operaii). Clasele UML sunt tipuri de date i corespund
claselor din limbajele Java, C++ i C#. O clas poate fi declarat abstract i n acest caz nu
poate fi instaniat la fel ca i n Java, C++ i C#.
O clas UML poate fi derivat din mai multe clase, la fel ca i n C++. Folosirea
motenirii multiple n model nu duce la o coresponden direct ntre model i cod n cazul
limbajelor Java sau C#.
O clas UML poate realiza/implementa mai multe interfee la fel ca i n Java sau C#.
Corespondena ntre modelele ce conin clase ce implementeaz mai multe interfee i C++
este realizat via clase C++ pur abstracte i motenire multipl.
Toate clasele din Figura 1.8sunt concrete, iar AbstractSaleRepository din Figura 1.7
este clas abstract (numele scris italic).
Principiul substituiei este aplicabil pentru instanele de tipul unor clase i interfee, la
fel ca i n Java, C++ i C#. Adic, instanele din program pot fi nlocuite cu instane ale
tipurilor derivate fr s alterm semantic programul.
C. Interfee
O interfa UML [29, 30] este un tip de date ce declar un set de operaii, adic un
contract pe care clasele pot s-l realizeze. Acest concept corespunde aceluiai concept din
Java/C# i claselor pur abstracte din C++.
SaleRepository din Figura 1.7 este o interfa. Atunci cnd evidenierea metodelor
interfeei nu este relevant, notaia grafic pentru interfee este cea din Figura 1.9.
Enumerrile UML [29, 30] descriu un set de simboluri care nu au asociate valori aa
cum aceleai concepte se regsesc n C++, Java i C#.
37
Tipurile structurate [29, 30] se modeleaz folosind stereotipul datatype i corespund
structurilor din C++/C# i tipurilor primitive din Java - a se vedea Error! Reference source
not found.. Instanele acestor tipuri sunt identificate doar prin valoarea lor. Ele sunt folosite
pentru a descrie proprietile claselor i corespund obiectelor valorice (ablonul value
object10), cu deosebirea c nu pot avea identitate.
Generalizarea [29, 30] este o relaie ntre un tip de date mai general (de baz) i unul
mai specializat (derivat). Aceast relaie poate fi aplicat ntre dou clase sau dou interfee,
corespunznd relaiilor de motenire din Java i C++/C# dintre clase, respectiv interfee
(clase pur abstracte n cazul C++).
Realizarea unei interfee n UML [29, 30] reprezint o relaie ntre o clas i o
interfa prin care se indic faptul c clasa este conform contractului specificat de interfa.
Aceste realizri corespund implementrilor interfeelor din Java i C#, respectiv motenirii n
C++. A se vedea notaiile grafice dintre AbstractSaleRepository i SaleRepository n Figura
1.7 i Figura 1.9.
F. Proprietai
Proprietile [29, 30] reprezint aspecte structurale ale unui tip de date. Proprietile
unei clase sunt introduse prin atribute i asocieri. Un atribut descrie o proprietate a clasei n
al doilea compartiment al ei, sub forma:
vizibilitate nume: tip multiplicitate = valoare {proprietati}
Numele este obligatoriu, la fel ca i vizibilitatea care poate fi publica (+), privat (-),
protetejat (#) sau la nivel de pachet (fr specificator). Vizibilitatea UML corespunde
specificatorilor de acces cu acelai nume din Java, avnd aceeai semantic. Vizibilitatea la
nivel de pachet nu se regasete n C++, iar n C# are o coresponden prin specificatorul
internal din C# dar care are i conotaii de distribuire a elementelor software (elementele
declarate internal n C# fiind accesibile doar n distribuia binar dll sau exe din care acestea
fac parte).
Celelalte elemente folosite la declararea unei proprieti sunt opionale. Tipul
proprietii poate fi oricare: clas, interfa, enumerare, tip structurat sau tip primitiv.
Tipurile primitive n UML sunt tipuri valorice [29]. UML definete urmtoarele tipuri
primitive: String, Integer, Boolean i UnlimitedNatural. Primele trei tipuri primitive sunt n
coresponden cu tipurile cu acelai nume din limbajele Java, C++ i C#, dar cu observaiile:
Tipul String este clas n Java i C#, instanele de tip String fiind nemodificabile, spre
deosebire de C++ unde irurile de caractere sunt modificabile. Codificarea
caracterelor nu este precizat n UML, n timp ce n Java i C# ea este Unicode, iar n
C++ ASCII.
Tipul Integer n UML este n precizie nelimitat, n timp ce n cele 3 limbaje plaja de
valori este limitat.
Multiplicitatea poate fi 1 (valoare implicit, atunci cnd multiplicitatea nu e precizat),
0..1 (optional), 0..* (zero sau mai multe valori), 1..* (unu sau mai multe valori), m..n (ntre m
i n valori, unde m i n sunt constante, n putnd fi *). Pentru o proprietate cu multiplicitate
m..* putem preciza n plus dac:
10
Martin Fowler. Patterns of Enterprise Application Architecture. Addison-Wesley, 2002.
38
Valorile se pot repeta sau nu - implicit valorile sunt unice (adic mulime), n caz
contrar precizm explicit prin nonunique (adic container cu valori posibil
duplicate).
Valorile pot fi referite prin indici sau nu - implicit nu (deci colecie), n caz contrar
precizm explicit ordered (deci list).
Exemple de proprieti:
multime : Integer[0..*] - mulime de valori ntregi (unice)
lista : Integer[0..*] {ordered} - list cu valori ntregi i distincte (unice)
lista : Integer[0..*] {ordered, nonunique} - list de ntregi
colectie : Integer[0..*] {nonunique} - colecie de ntregi
Proprietilor din UML le corespund cmpuri sau variabile de tip obiect n Java i C++,
respectiv proprieti n C#. Dificulti de interpretare se ridic n ceea ce privete proprietile
cu multiplicitate m..*. Pentru exemplele de mai sus putem considera urmtoarele
corespondene cu Java (in mod similar i cu C++/C#):
mulimi de ntregi:
o int[] multime sau Integer[] multime, urmnd s asigurm prin operaii c
multime va conine valori distincte, sau cel mai potrivit
o java.util.Set multime
liste cu valori ntregi i distincte:
o int[] lista, Integer[] lista sau java.util.List lista, urmnd s asigurm prin
operaii c lista va conine valori distincte
liste de ntregi:
o int[] lista, Integer[] lista, sau java.util.List lista
colecii de ntregi:
o int[] colectie, Integer[] colectie, sau java.util.Collection colectie
Asocierile UML [29, 30] reprezint un set de tuple, fiecare tuplu fcnd legtura ntre
dou instane ale unor tipuri de date. n acest sens, o asociere este un tip de date care leag
proprieti ale altor tipuri de date.
Figura 1.10 (a) prezint modelul rezultat dup adugarea atributelor quantity i product
n clasa SaleItem reprezentat grafic n diagrama (b). Codul (d) scris n Java/C# corespunde
acestei situaii. Dac considerm c e mai potrivit o reprezentare grafic pentru relaia dintre
clasele SaleItem i Product, atunci n loc s adugm product ca i atribut, folosim o asociere
unidirecional de la SaleItem spre Product. Atunci cnd se adaug asocierea unidirecional,
n model se creeaz o asociere i o proprietate n clasa SaleItem, avnd numele rolului, adic
product. Astfel, codul (d) corespunde reprezentrii grafice (c) a modelului (a) care mai
conine o asociere neartat n figur. Asocierile unidirecionale introduc proprieti n
clasa surs, de tipul clasei destinaie. Numele proprietii coincide cu numele rolului
asocierii, iar forma general de definire a proprietilor (prezentat la nceputul acestei
subseciuni) se aplic i n acest caz.
39
Decizia folosirii asocierilor n locul atributelor este luat n funcie de context. De
exemplu, atunci cnd modelm entitile unui aplicaii folosim asocieri pentru a indica
relaiile dintre entiti i folosim atribute atunci cnd descriem entitile folosind obiecte
valorice/descriptive. n general, folosim asocieri cnd dorim s evideniem importana
tipurilor i a legturilor dintre ele.
Asocierile bidirecionale leag dou proprieti din dou clase diferite sau din aceeai
clas. Figura 1.11 prezint o asociere bidirecional ntre SaleItem i Product, precum i
codul Java/C# corespunztor acestei situaii.
G. Dependene
ntre dou elemente software, client i furnizor, exist o dependen [29, 30] dac
schimbarea definiiei furnizorului poate duce la schimbarea clientului. De exemplu dac o
clas C trimite un mesaj altei clase F, atunci C este dependent de F deoarece schimbarea
definiiei mesajului n F va implica schimbri n C privind modul de transmitere. Ca regul
general, ar trebui s minimizm dependenele n model, n timp ce pstrm coezive aceste
elemente.
n UML se pot indica explicit dependenele ntre orice elemente din model. Prezentarea
lor explicit ns poate face modelul greu de citit. Din acest motiv, dependenele se prezint
explicit n mod selectiv, evideniind elementele principale i arhitecturale - a se vedea Figura
1.6 i Figura 1.7.
H. Operaii
40
Operaiile n UML [29, 30] definesc comportamentul obiectelor i corespund
metodelor din limbajele de programare orientate obiect. De fapt, operaiile specific
comportamentul (reprezint antetul), iar corpul/implementarea este definit de elemente
comportamentale ca i interaciuni, maini cu stri i activiti - implementrile sunt numite
metode n UML. Sintaxa specificrii operaiilor este:
vizibilitate nume (lista-parametri) : tip-returnat {proprieti}
unde vizibilitatea, tipul-returnat i proprietile sunt definite ca i n cazul proprietilor
claselor. n lista proprietilor operaiei se poate preciza dac este doar o operaie de
interogare {query}, adic o operaie ce nu modific starea obiectului apelant - implicit,
operaiile sunt considerate comenzi, adic modific starea obiectelor. Parametrii n lista-
parametrilor sunt separai prin virgul, un parametru fiind de forma:
direcie nume: tip = valoare-implicit,
direcia putnd fi: in, out i in-out, implicit fiind in.
Ca i n Java, C++ i C#, putem defini operaii statice sau de tip clas, n diagrame
acestea fiind reprezentate prin subliniere.
41
Figura 1.12 Interfaa sistemului
Diagrame de tipul celei prezentate n Figura 1.12 pot fi definite n context CIM, nainte
de stabilirea unei arhitecturi. La nivel PIM, odat identificat ceea ce trebuie s fac
sistemul, se folosesc diagrame de secven pentru a detalia cum vor colabora obiectele din
sistem, conform responsabilitilor precizate prin arhitectura stabilit. Figura 1.13 prezint
detaliat colaborarea n cazul sistemului POS.
Participanii din Figura 1.12 nu indic instane ale unor tipuri din model. De ast dat,
participanii principali din Figura 1.13 sunt obiecte controller, service i repository, conform
arhitecturii POS. Diagrama prezint mesaje adresate ctre controller (1, 3, 6 i 9) deoarece n
diagram nu e prezent i elementul de interfa utilizator.
Participanii fiind obiecte, mesajele transmise vor fi apeluri de metode ale obiectelor
spre care sunt trimise mesajele. Astfel, diagrama ne conduce la identificarea metodelor
obiectelor. Figura 1.14 prezint metodele identificate pe baza interaciunilor din Figura
1.13.
42
Figura 1.13 Proiectare detaliat - interaciuni ntre obiecte
In cele ce urmeaz vom prezenta dou dintre containerele des folosite in programare i
anume listele i dicionarele. Vom specifica tipurile abstracte de date corespunztoare,
indicnd i specificnd operaiile caracteristice. Pentru fiecare operaie din interfaa unui tip
de date, vom da specificarea operaiei n limbaj natural, indicnd datele i precondiiile
operaiei (pre), precum i rezultatele i postcondiiile operaiei (post).
43
1.5.1. Liste
In limbajul uzual cuvntul list refer o nirare, ntr-o anumit ordine, a unor
nume de persoane sau de obiecte, a unor date etc. Exemple de liste sunt multiple: list de
cumprturi, list de preuri, list de studeni, etc. Ordinea n list poate fi interpretat ca un
fel de legtur ntre elementele listei (dup prima cumprtur urmeaz a doua
cumprtur, dup a doua cumprtur urmeaz a treia cumprtur, etc) sau poate fi vzut
ca fiind dat de numrul de ordine al elementului n list (1-a cumprtur, a 2-a
cumprtur, etc). Tipul de date List care va fi definit n continuare permite
implementarea n aplicaii a acestor situaii din lumea real.
Ca urmare, o list o putem vedea ca pe o secven de elemente l1 , l 2 ,.., l n de un
acelai tip (TElement) aflate ntr-o anumit ordine, fiecare element avnd o poziie bine
determinat n cadrul listei. Ca urmare, poziia elementelor n cadrul listei este esenial,
astfel accesul, tergerea i adugarea se pot face pe orice poziie n list. Lista poate fi vzut
ca o colecie dinamic de elemente n care este esenial ordinea elementelor. Numrul n de
elemente din list se numete lungimea listei. O list de lungime 0 se va numi lista vid.
Caracterul de dinamicitate al listei este dat de faptul c lista i poate modifica n timp
lungimea prin adugri i tergeri de elemente n/din list.
In cele ce urmeaz, ne vom referi la listele liniare. O list liniar, este o structur care
fie este vid (nu are nici un element), fie
are un unic prim element;
are un unic ultim element;
fiecare element din list (cu excepia ultimului element) are un singur succesor;
fiecare element din list (cu excepia primului element) are un singur predecesor.
Ca urmare, ntr-o list liniar se pot insera elemente, terge elemente, se poate
determina succesorul (predecesorul) unui element, se poate accesa un element pe baza
poziiei sale n list.
O list liniar se numete circular dac se consider predecesorul primului nod a fi
ultimul nod, iar succesorul ultimului nod a fi primul nod.
Conform definiiei anterioare, fiecare element al unei listei liniare are o poziie bine
determinat n list. De asemenea, este important prima poziie n cadrul listei, iar dac se
cunoate poziia unui element din list atunci pe baza aceastei poziii se poate identifica
elementul din list, poziia elementului predecesor i poziia elementului succesor n list
(dac acestea exist). Ca urmare, ntr-o list se poate stabili o ordine ntre poziiile
elementelor n cadrul listei.
Poziia unui element n cadrul listei poate fi vzut n diferite moduri:
1. ca fiind dat de rangul (numrul de ordine al) elementului n cadrul listei. n acest
caz este o similitudine cu tablourile, poziia unui element n list fiind indexul
acestuia n cadrul listei. ntr-o astfel de abordare, lista este vzut ca un tablou
dinamic n care se pot accesa/aduga/terge elemente pe orice poziie n list.
2. ca fiind dat de o referin la locaia unde se stocheaz elementul listei (ex:
pointer spre locaia unde se memoreaz elementul).
Pentru a asigura generalitatea, vom abstractiza noiunea de poziie a unui element n
list i vom presupune c elementele listei sunt accesate prin intermediul unei poziii
generice.
44
Vom spune c o poziie p ntr-o list este valid dac este poziia unui element al
listei. Spre exemplu, dac p ar fi un pointer spre locaia unde se memoreaz un element al
listei, atunci p este valid dac este diferit de pointerul nul sau de orice alt adres care nu
reprezint adresa de memorare a unui element al listei. n cazul n care p ar fi rangul (numrul
de ordine al) elementului n list, atunci p este valid dac nu depete numrul de elemente
din list.
Ca urmare, dac ne gndim la o list liniar n care operaiile de
acces/inserare/tergere s se fac pe baza unei poziii generice n list, se ajunge la urmtorul
tip abstract de date.
Lista vid o vom nota n ceea ce urmeaz cu .
domeniu
L={l | l este o list cu elemente de tip TElement}
operaii (interfaa minimal)
creeaz(l)
descriere: se creeaz o list vid
pre: adevrat
post: lL, l
adaugSfarsit (l, e)
descriere: se adaug un element la sfritul listei
pre: lL, eTElement
post: lL, l este l n care a fost adugat e la sfrit
adaugInceput(l, e)
descriere: se adaug un element la nceputul listei
pre: lL, eTElement
post: lL, l este l n care a fost adugat e la nceput
valid(l, p)
descriere: funcie care verific dac o poziie n list este valid
pre: lL, p e o poziie n l
post: valid= adevrat dac p este o poziie valid n l
fals n caz contrar
adaugnainte(l, p, e)
descriere: se adaug un element naintea unei anumite poziii n list
45
pre: lL, eTElement, p e o poziie n l, valid(l, p)
post: lL, l este l n care a fost inserat e nainte de poziia p
adaugDup(l, p, e)
descriere: se adaug un element dup o anumit poziie n list
pre: lL, eTElement, p e o poziie n l, valid(l, p)
post: lL, l este l n care a fost inserat e dup poziia p
terge (l, p, e)
descriere: se terge elementul din list situat pe o anumit poziie
pre: lL, eTElement, p e o poziie n l, valid(l, p)
post: eTElement, lL, l este l din care a fost ters elementul de pe poziia
p, e este elementul ters
element (l, p, e)
descriere: accesarea elementului din list de pe o anumit poziie
pre: lL, eTElement, p e o poziie n l, valid(l, p)
post: eTElement, e este elementul de pe poziia p din l
modifica (l, p, e)
descriere: modificarea elementului din list de pe o anumit poziie
pre: lL, eTElement, p e o poziie n l, valid(l, p)
post: lL, l este l n care s-a nlocuit elementul de pe poziia p cu e
prim(l)
descriere: funcie care returneaz poziia primului element n list
pre: lL
post: prim= poziia primului element din l sau o poziie care nu e valid
dac l e vid
ultim(l)
descriere: funcie care returneaz poziia ultimului element n list
pre: lL
post: ultim= poziia ultimului element din l sau o poziie care nu e valid
dac l e vid
urmtor(l, p)
descriere: funcie care returneaz poziia n list urmtoare unei poziii
date
pre: lL, p e o poziie n l, valid(l, p)
post: urmator= poziia din l care urmeaz poziiei p sau o poziie care nu e
valid dac p e poziia ultimului element din list
precedent(l, p)
descriere: funcie care returneaz poziia n list precedent unei poziii
date
pre: lL, p e o poziie n l, valid(l, p)
post: precedent= poziia din l care precede poziia p sau o poziie care nu
e valid dac p e poziia primului element din list
caut(l, e)
descriere: funcie care caut un element n list
46
pre: lL, eTElement
post: caut = prima poziie pe care apare e n l sau o poziie care nu e
valid dac e l
apare(l, e)
descriere: funcie care verific apartenena unui element n list
pre: lL, eTElement
post: apare = adevrat e l
fals contrar
vid(l)
descriere: funcie care verific dac lista este vid
pre: lL
post: vid = adevrat n cazul n care l e lista vid
fals n caz contrar
dim(l)
descriere: funcie care returneaz numrul de elemente din list
pre: lL
post: dim=numrul de elemente din list
iterator(l, i)
descriere: se construiete un iterator pe list
pre: lL
post: i este un iterator pe lista l
distruge(l)
descriere: distruge o list
pre: lL
post: lista l a fost distrus
Reamintim modul n care va putea fi tiprit o list (ca orice alt container care poate fi
iterat) folosind iteratorul construit pe baza operaiei iterator din interfaa listei.
Subalgoritmul tiprire(l) este:
{pre: l este o list}
{post: se tipresc elementele listei}
iterator(l, i) {se obine un iterator pe lista l}
Cttimp valid(i) execut {ct timp iteratorul e valid}
element(i, e) {e este elementul curent referit de iterator}
@ tiprete e {se tiprete elementul curent}
urmtor(i) {iteratorul refer urmtorul element}
sfct
sf-tiprire
Observaie
adaugSfarsit (l, e)
desc.: se adaug un element la sfritul listei
47
pre: lL, eTElement
post: lL, l = l {e}, e este pe ultima poziie n l
adaugSfarsit (l, e)
descriere: se adaug un element la sfritul listei
pre: lL, eTElement
post: lL, l este modificat prin adugarea lui e la sfrit i pstrarea celorlate
elemente pe poziiile lor
1.5.2. Dicionare
Dicionarele reprezint containere coninnd elemente sunt forma unor perechi (cheie,
valoare). Dicionarele pstreaz elemente n aa fel nct ele s poat fi uor localizate
folosind chei. Operaiile de baz pe dicionare sunt cutare, adugare i tergere elemente.
ntr-un dicionar cheile sunt unice i n general, o cheie are o unic valoare asociat.
domeniu
D={d | d este un dicionar cu elemente e = (c, v), c de tip TCheie, v de tip TValoare}
creeaz(d)
descriere: se creeaz un dicionar vid
pre: true
post:dD, d este dicionarul vid (fr elemente)
48
adaug(d, c, v)
descriere: se adaug un element n dicionar
pre: dD, cTCheie, vTValoare
post: dD, d=d{c, v} (se adaug n dicionar perechea (c, v))
caut(d, c)
descriere: se adaug un element n dicionar (dup cheie)
pre: dD, cTCheie
post: caut= vTValoare dac (c,v)d
elementul nul al TValoare n caz contrar
terge(d, c)
descriere: se adaug un element n dicionar (dup cheie)
pre: dD, cTCheie
post: terge= vTValoare dac (c,v)d, d este d din care a fost ters
perechea (c,v)
elementul nul al TValoare n caz contrar
dim(d)
descriere: funcie care returneaz numrul de elemente din list
pre: dD
post: dim= dimensiunea dicionarului d (numrul de elemente) N*
vid(d)
descriere: funcie care verific dac dicionarul este vid
pre: dD
post: vid= adevrat n cazul n care d e dicionarul vid
fals n caz contrar
chei(d, m)
descriere: se determin mulimea cheilor din dicionar
pre: dD
post: mM, m este mulimea cheilor din dicionarul d
valori(d, c)
descriere: se determin colecia valorilor din dicionar
pre: dD
post: cCol, c este colecia valorilor din dicionarul d
perechi(d, m)
descriere: se determin mulimea perechilor (cheie, valoare) din dicionar
pre: dD
post: mM, m este mulimea perechilor (cheie, valoare) din dicionarul d
iterator(d, i)
descriere: se creeaz un iterator pe dicionar
pre: d D
post:i I, i este iterator pe dicionarul d
distruge(d)
descriere: distruge un dicionar
pre: d D
49
post: dicionarul d a fost distrus
Reamintim modul n care va putea fi tiprit un dicionar (ca orice alt container care
poate fi iterat) folosind iteratorul construit pe baza operaiei iterator din interfaa
dicionarului.
Subalgoritmul tiprire(d) este:
{pre: d este un dicionar}
{post: se tipresc elementele dicionarului}
iterator(d, i) {se obine un iterator pe dicionarul d}
Cttimp valid(i) execut {ct timp iteratorul e valid}
element(i, e) {e este elementul curent referit de iterator}
@ tiprete e {se tiprete elementul curent}
urmtor(i) {iteratorul refer urmtorul element}
sfct
sf-tiprire
1. Scriei un program ntr-unul din limbajele de programare Python, C++, Java, C# care:
a. Definete o clas B avnd un atribut b de tip ntreg i o metod de tiprire care
afieaz atributul b la ieirea standard.
b. Definete o clas D derivat din B avnd un atribut d de tip ir de caractere i de
asemenea o metod de tiprire pe ieirea standard care va afia atributul b din
clasa de baz i atributul d.
c. Definete o funcie care construiete o list coninnd: un obiect o1 de tip B avnd
b egal cu 8; un obiect o2 de tip D avnd b egal cu 5 i d egal cu D5; un obiect o3
de tip B avnd b egal cu -3; un obiect o4 de tip D avnd b egal cu 9 i d egal cu
D9.
d. Definete o funcie care primete o list cu obiecte de tip B i returneaz o list
doar cu obiectele care satisfac proprietatea: b>6.
e. Pentru tipul de dat list utilizat n program, scriei specificaiile operaiilor
folosite.
Se pot folosi biblioteci existente pentru structuri de date (Python, C++, Java, C#). Nu se
cere implementare pentru operaiile listei.
2. Scriei un program ntr-unul din limbajele de programare Python, C++, Java, C# care:
a. Definete o clas B avnd un atribut b de tip ntreg i o metod de tiprire care
afieaz atributul b la ieirea standard.
b. Definete o clas D derivat din B avnd un atribut d de tip ir de caractere i de
asemenea o metod de tiprire pe ieirea standard care va afia atributul b din
clasa de baz i atributul d.
c. Definete o funcie care construiete un dicionar coninnd: un obiect o1 de tip B
avnd b egal cu 8; un obiect o2 de tip D avnd b egal cu 5 i d egal cu D5; un
obiect o3 de tip B avnd b egal cu -3; un obiect o4 de tip D avnd b egal cu 9 i d
egal cu D9. (cheia unui obiect din dicionar este valoarea b, iar valoarea
asociat cheii este obiectul).
d. Definete o funcie care primete un dicionar cu obiecte de tip B i verific dac
n dicionar exist un obiect care satisface proprietatea: b>6.
50
e. Pentru tipul de dat dicionar utilizat n program, scriei specificaiile operaiilor
folosite.
Se pot folosi biblioteci existente pentru structuri de date (Python, C++, Java, C#). Nu se
cere implementare pentru operaiile dicionarului.
51
2. Baze de date
2.1. Baze de date relaionale. Primele trei forme normale ale unei
relaii
R A1 ... Ai .. An
r1 a11 ... a1j ... a1n
... ... ... ... ... ...
ri ai1 ... aij ... ain
... ... ... ... ... ...
rm am1 ... amj ... amn
unde liniile din acest tabel formeaz elementele relaiei, sau tupluri, sau nregistrri, care
n general sunt distincte, i aij D j , j 1,...,n, i 1,...,m. Deoarece modul n care se
evideniaz elementele relaiei R de mai sus seamn cu un tabel, relaia se mai numete i
tabel. Pentru a pune n eviden numele relaiei (tabelului) i lista atributelor vom nota
aceast relaie cu:
RA1 , A2 ,..., An .
Modelul relaional al unei baze de date const dintr-o colecie de relaii ce variaz n
timp (coninutul relaiilor se poate schimba prin operaii de adugare, tergere i actualizare).
O baz de date relaional const din trei pri:
1. Datele (relaii sau tabele, legturi ntre tabele) i descrierea acestora;
52
2. Reguli de integritate (pentru a memora numai valori corecte n relaii);
3. Operatori de gestiune a datelor.
Exemplul 1. STUDENTI [NUME, ANUL_NASTERII, ANUL_DE_STUDIU],
cu urmtoarele valori posibile:
53
atributului A se caut printre valorile cheii din relaia R1. Cele dou relaii R1 i R2 nu este
obligatoriu s fie distincte.
A=cheie extern
R1 chei R2
e
v v
Exemplu:
CLASE [cod, profil]
ELEVI [nrmatricol, nume, clasa, datanasterii].
Legtura o putem stabili ntre relaia CLASE (considerat ca printe pentru legtur) i
relaia ELEVI (ca membru pentru legtur) prin egalitatea CLASE.cod=ELEVI.clasa. Unei
anumite clase (memorat n relaia CLASE), identificat printr-un cod, i corespund toi
elevii din clasa cu codul respectiv.
Prin cheie extern se pot memora legturi 1:n ntre entiti: la o clas corespund
orici elevi, iar unui elev i este asociat cel mult o clas.
Cheia extern se poate folosi i pentru a memora legturi m:n ntre entiti.
Fie dou entiti: discipline i studeni. La o disciplin sunt "inscrii" mai muli studeni, iar
un student are asociate mai multe discipline. Varianta de memorare cuprinde o relaie
intermediar.
Pentru ca valorile dintr-o baz de date s fie corecte, la definirea bazei de date se pot
preciza anumite restricii de intergritate (ele sunt verificate de sistemul de gestiune a bazei
de date la modificarea datelor din tabele). Aceste restricii se refer la o coloan, la un tabel,
la o legtur ntre dou tabele:
restricii asociate coloanei:
o Not Null - coloana nu poate s primeasc valori nedefinite
o Primary Key - coloana curent se definete cheia primar
o Unique - valorile coloanei sunt unice
54
o Check(condiie) - se d condiia pe care trebuie s o ndeplineasc valorile
coloanei (condiii simple, care au valoarea true sau false)
o Foreign Key REFERENCES tabel_parinte [(nume_coloana)] [On Update actiune]
[On Delete actiune] - coloana curent este cheie extern
restricii asociate tabelului:
o Primary key(lista coloane) - definirea cheii primare pentru tabel
o Unique(lista coloane) - valorile sunt unice pentru lista de coloane precizat
o Check(condiie) - pentru a preciza condiia pe care trebuie s o ndeplineasc
valorile unei linii
o Foreign Key nume_cheie_externa(lista_coloane) REFERENCES tabel_parinte
[(lista_coloane)] [On Update actiune] [On Delete actiune] - se definete cheia
extern
In general anumite date se pot reprezenta n mai multe moduri prin relaii (la modelul
relaional). Pentru ca aceste date s se poat prelucra ct mai simplu (la o operaie de
actualizare a datelor s nu fie necesare teste suplimentare) este necesar ca relaiile n care se
memoreaz datele s verifice anumite condiii (s aib un anumit nivel de normalizare).
Pn n prezent se cunosc mai multe forme normale pentru relaii, dintre care cele mai
cunoscute sunt: 1NF, 2NF, 3NF, BCNF, 4NF, 5NF. Avem urmtoarele incluziuni pentru
relaii n diferite forme normale:
1NF
2NF
3NF
BCNF
4NF
5NF
A1, A2 ,..., An . Prin proiecia relaiei R pe se nelege relaia:
R' Ai1 , Ai2 ,..., Ai p ( R ) A
i1 , Ai2 ,..., Ai p ( R ), ,
unde:
r a1 , a2 ,...,an R (r ) r ai1 , ai 2 ,...,ai p R' ,
i toate elementele din R' sunt distincte.
55
Definiie. Pentru compunerea relaiilor se folosete operatorul de join natural. Fie
R , , S , dou relaii peste mulimile de atribute , , , . Prin joinul
natural al relaiilor R i S se nelege relaia:
i dou noi relaii obinute prin proiecia acestei relaii: SC[Student, CadruDidactic] i
CD[CadruDidactic, Disciplina]. Presupunem c pentru relaia iniial avem urmtoarele
valori:
R Student CadruDidactic Disciplina
r1 s1 c1 d1
r2 s2 c2 d2
r3 s1 c2 d3
Folosind definiia proieciei se obin urmtoarele valori pentru cele dou relaii obinute
din R i pentru joinul natural al acestor relaii:
SC Student CadruDidactic
r1 s1 c1
r2 s2 c2
r3 s1 c2
CD CadruDidactic Disciplina
r1 c1 d1
r2 c2 d2
r3 c2 d3
56
Este posibil ca n diverse aplicaii practice s apar atribute (simple sau compuse) ce
iau mai multe valori pentru un element din relaie. Aceste atribute formeaz un atribut
repetitiv.
Exemplul 4. Fie relaia:
STUDENT [NUME, ANULNASTERII, GRUPA, DISCIPLINA, NOTA],
cu atributul NUME ca i cheie. In acest exemplu perechea {DISCIPLINA, NOTA} este un
grup repetitiv. Putem avea urmtoarele valori n aceast relaie:
NUME ANULNASTERII GRUPA DISCIPLINA NOTA
Pop Ioan 1998 221 Baze de date 10
Sisteme de operare 9
Probabiliti 8
Murean Ana 1999 222 Baze de date 8
Sisteme de operare 7
Probabiliti 10
Proiect individual 9
Exemplul 5. Fie relaia:
CARTE [Cota, NumeAutori, Titlu, Editura, AnApariie, Limba, CuvinteCheie],
cu atributul Cota ca i cheie i atributele repetitive NumeAutori i CuvinteCheie. Atributul
Cota poate avea o semnificaie efectiv (s existe o cot asociat la fiecare carte) sau s fie
introdus pentru existena cheii (valorile s fie distincte, eventual pot s fie generate automat).
Grupurile repetitive creaz foarte multe greuti n memorarea diverselor relaii i din
aceast cauz se ncearc evitarea lor, fr ns a pierde date. Dac R[A] este o relaie, unde
A este mulimea atributelor, iar formeaz un grup repetitiv (atribut simplu sau compus),
atunci R se poate descompune n dou relaii fr ca s fie atribut repetitiv. Dac C este o
cheie pentru relaia R, atunci cele dou relaii n care se descompune relaia R sunt:
R' C C R i R' ' A A R .
57
Sistemele de gestiune a bazelor de date relaionale permit descrierea numai a relaiilor
ce se afl n 1NF. Exist i sisteme ce permit gestiunea relaiilor non-1NF (exemplu Oracle,
unde o coloan poate fi un obiect sau o colecie de date, sau mai recent bazele de date
NoSQL).
Urmtoarele forme normale ale unei relaii utilizeaz o noiune foarte important, i
anume dependena funcional dintre diverse submulimi de atribute. Stabilirea
dependenelor funcionale este o sarcin a administratorului bazei de date i depinde de
semnificaia (semantica) datelor ce se memoreaz n relaie. Operaiile de actualizare a
datelor din relaie (nserare, tergere, modificare) nu trebuie s modifice dependenele
funcionale (dac pentru relaie exist astfel de dependene).
Definiie. Fie RA1 , A2 ,..., An o relaie i , A1 , A2 ,..., An dou submulimi de
atribute. Atributul (simplu sau compus) este dependent funcional de atributul (simplu
sau compus), notaie: , dac i numai dac fiecare valoare a lui din R are asociat
o valoare precis i unic pentru (aceast asociere este valabil tot timpul existenei
relaiei R). O valoare oarecare a lui poate s apar n mai multe linii ale lui R i atunci
fiecare dintre aceste linii conine aceeai valoare pentru atributul , deci:
58
de date va conine erori (va fi inconsistent). Dac la prima nregistrare se schimb
valoarea atributului CadruDidactic i nu se face aceeai modificare i la nregistrrile 4 i
5, atunci modificarea va introduce o eroare n relaie.
Anomalii la nserare: la adugarea unei nregistrri trebuie s se cunoasc valorile
atributelor, nu se pot folosi valori nedefinite pentru atributele implicate n dependenele
funcionale.
Anomalii la tergere: la tergerea unor nregistrri se pot terge i asocieri (ntre valori)
ce nu se pot reface. De exemplu, dac se terg nregistrrile 2 i 3, atunci asocierea dintre
Disciplina i CadruDidactic se pierde.
Anomaliile de mai sus apar datorit existenei unei dependene funcionale ntre
mulimi de atribute. Pentru a elimina situaiile amintite trebuie ca aceste dependene
(asocieri) de valori s se pstreze ntr-o relaie separat. Pentru aceasta este necesar ca relaia
iniial s se descompun, fr ca prin descompunere s se piard date sau s se introduc
date noi prin compunerea de relaii (trebuie ca descompunerea "s fie bun"). O astfel de
descompunere se face n momentul proiectrii bazei de date, cnd se pot stabili dependenele
funcionale.
Observaii. Se pot demonstra uor urmtoarele proprieti simple pentru dependenele
funcionale:
1. Dac C este o cheie pentru RA1 , A2 ,..., An , atunci C , A1 , A2 ,..., An .
2. Dac , atunci , numit dependena funcional trivial sau reflexivitatea.
( r1 ) ( r2 ) ( r1 ) ( r2 )
3. Dac , atunci , cu .
( r1 ) ( r2 ) ( r1 ) ( r2 ) ( r1 ) ( r2 )
Definiie. Un atribut A (simplu sau compus) se numete prim dac exist o cheie C i
A C (C este o cheie compus, sau A este chiar o cheie a relaiei). Dac un atribut nu este
inclus n nici o cheie, atunci se numete neprim.
Definiie. Fie RA1 , A2 ,..., An i , A1 , A2 ,..., An . Atributul este complet
dependent funcional de dac este dependent funcional de (deci ) i nu
este dependent funcional de nici o submulime de atribute din ( , nu este
adevrat).
Observaie. Dac atributul nu este complet dependent funcional de (deci
este dependent de o submulime a lui ), atunci este un atribut compus.
Definiie. O relaie este de a doua form norml (2NF) dac:
este de prima form normal,
59
orice atribut neprim (simplu sau compus) (deci care nu este inclus ntr-o cheie) este
complet dependent funcional de oricare cheie a relaiei.
Observaie. Dac o relaie este de prima form normal (1NF) i nu este de a doua
form normal (2NF), atunci are o cheie compus (dac o relaie nu este de a doua form
normal, atunci exist o dependen funcional cu atribut inclus ntr-o cheie).
Pentru a preciza modul de descompunere pentru cazul general, fie RA1, A2 ,..., An o
relaie i C A A1 , A2 ,..., An o cheie. Presupunem c exist A , C ( este
un atribut necheie), dependent funcional de C ( este complet dependent
funcional de o submulime strict de atribute din cheie). Dependena se poate
elimina dac relaia R se descompune n urmtoarele dou relaii:
R' R i R' ' A R .
A
60
Exemplul 10. Rezultatele obinute de absolveni la lucrarea de licen sunt trecute n
relaia:
LUCRARI_LICENTA [NumeAbsolvent, Nota, CadruDidIndr, Departament].
Aici se memoreaz numele cadrului didactic ndrumtor i denumirea departamentului la care
se afl acesta. Deoarece se introduc date despre absolveni, cte o nregistrare pentru un
absolvent, putem s stabilim c NumeAbsolvent este cheia relaiei. Din semnificaia
atributelor incluse n relaie se observ urmtoarea dependen funcional:
{CadruDidIndr} {Departament}.
Din existena acestei dependene funcionale se deduce c relaia nu este de 3NF.
Pentru a elimina dependena funcional, relaia se poate descompune n urmtoarele dou
relaii:
REZULTATE [NumeAbsolvent, Nota, CadruDidIndr]
INDRUMATORI [CadruDidIndr, Departament].
Exemplul 11. Presupunem c adresele unui grup de persoane se memoreaz n urmtoarea
relaie:
ADRESE [CNP, Nume, Prenume, CodPostal, LocalitateDomiciliu, Strada, Nr].
Cheia relaiei este {CNP}. Deoarece la unele localiti codul potal se stabilete la nivel de
strad, sau chiar poiuni de strad, exist dependena funcional:
{CodPostal} {LocalitateDomiciliu}.
Deoarece exist aceast dependen funcional, deducem c relaia ADRESE nu este de a
treia form normal, deci este necesar descompunerea ei.
Exemplul 12. S considerm urmtoarea relaie care memoreaz o eventual planificare a
studenilor pentru examene:
PLANIFICARE_EX [Data, Ora, Cadru_did, Sala, Grupa],
cu urmtoarele restricii (cerine care trebuie respectate) i care se transpun n definirea de
chei sau de dependene funcionale:
1. Un student d maximum un examen ntr-o zi, deci {Grupa, Data} este cheie.
2. Un cadru didactic are examen cu o singur grup la o anumit or, deci {Cadru_did, Data,
Ora} este cheie.
3. La un moment dat ntr-o sal este planificat cel mult un examen, deci {Sala, Data, Ora}
este cheie.
4. Intr-o zi cadrul didactic nu schimb sala, n sala respectiv pot fi planificate i alte
examene, dar la alte ore, deci exist urmtoarea dependen funcional:
{Cadru_did, Data} {Sala}
Toate atributele din aceast relaie apar n cel puin o cheie, deci nu exist atribute
neprime. Avnd n vedere definiia formelor normale precizate pn acuma, putem spune c
relaia este n 3NF. Pentru a elimina i dependenele funcionale de tipul celor pe care le
avem n exemplul de mai sus s-a introdus o nou form normal:
Definiie. O relaie este n 3NF Boyce-Codd, sau BCNF, dac orice determinant
(pentru o dependen funcional) este cheie, deci nu exist dependene funcionale
astfel nct s nu fie cheie.
61
Pentru a elimina dependena funcional amintit mai sus trebuie s facem urmtoarea
descompunere pentru relaia PLANIFICARE_EX:
PLANIFICARE_EX [Data, Cadru_did, Ora, Student],
REPARTIZARE_SALI [Cadru_did, Data, Sala].
Dup aceast descompunere nu mai exist dependene funcionale, deci relaiile sunt de
tipul BCNF, dar a disprut cheia asociat restriciei precizate la punctul 3 de mai sus: {Sala,
Data, Ora}. Dac se mai dorete pstrat o astfel de restricie, atunci ea trebuie verificat
altfel (de exemplu, prin program).
Pentru a explica limbajul de interogare (cererea de date) bazat pe algebra relaiilor vom
preciza la nceput tipurile de condiii ce pot apare n cadrul diferiilor operatori relaionali.
1. Pentru a verifica dac un atribut ndeplinete o condiie simpl se face compararea acestuia
cu o anumit valoare, sub forma:
nume atribut operator_relaional valoare
2. O relaie cu o singur coloan poate fi considerat ca o mulime. Urmtoarea condiie
testeaz dac o anumit valoare aparine sau nu unei mulimi:
IS IN
nume_atribut relaie_cu_o_coloan
IS NOT IN
3. Dou relaii (considerate ca mulimi de nregistrri) se pot compara prin operaiile de
egalitate, diferit, incluziune, neincluziune. Intre dou relaii cu acelai numr de
coloane i cu aceleai tipuri de date pentru coloane (deci ntre dou mulimi
comparabile) putem avea condiii de tipul urmtor:
IS IN
IS NOT IN
relaie relaie
62
expresie - dac se evalueaz expresia, iar dac apar i denumiri de atribute, atunci acestea
se iau dintr-o nregistrare curent.
COUNT(*) FROM relaie - precizeaz numrul de nregistrri din relaia specificat.
COUNT
SUM
AVG DISTINCT nume _ atribut - care determin o valoare plecnd de la toate
MAX
MIN
nregistrrile din relaia curent. La determinarea acestei valori se iau toate valorile
atributului precizat ca argument (din toate nregistrrile), sau numai valorile distincte,
dup cum lipsete sau apare cuvntul DISTINCT. Valorile astfel determinate sunt:
numrul de valori (pentru COUNT), suma acestor valori (apare SUM, valorile trebuie s
fie numerice), valoarea medie (apare AVG, valorile trebuie s fie numerice), valoarea
maxim (apare MAX), respectiv valoarea minim (apare MIN).
In continuare se vor preciza operatorii care se pot folosi pentru interogarea bazelor
de date relaionale.
Selecia (sau proiecia orizontal) a unei relaii R - determin o nou relaie ce are aceeai
schem cu a relaiei R. Din relaia R se iau numai nregistrrile care ndeplinesc o
condiie c. Notaia pentru acest operator este: c (R) .
Proiecia (sau proiecia vertical) - determin o relaie nou ce are atributele precizate
printr-o mulime de atribute. Din fiecare nregistrare a unei relaii R se determin numai
valorile atributelor incluse n mulimea . Mulimea de atribute se poate extinde la o
mulime de expresii (n loc de o mulime de atribute), care precizeaz coloanele relaiei
care se construiete. Notaia pentru acest operator este: (R) .
Produsul cartezian a dou relaii: R1R2 - care determin o relaie nou ce are ca
atribute concatenarea atributelor din cele dou relaii, iar fiecare nregistrare din R 1 se
concateneaz cu fiecare nregistrare din R2.
Reuniunea, diferena i intersecia a dou relaii: R1 R2, R1 - R2, R1 R2. Cele dou
relaii trebuie s aib aceeai schem.
Exist mai muli operatori join.
Joinul condiional sau theta join, notat prin R1 R2 - care determin acele
nregistrri din produsul cartezian al celor dou relaii care ndeplinesc o anumit
condiie. Din definiie se observ c avem: R1 R2= ( R1 R2 ) .
Joinul natural, notat prin R1 R2, care determin o relaie nou ce are ca atribute
reuniunea atributelor din cele dou relaii, iar nregistrrile se obin din toate perechile
de nregistrri ale celor dou relaii care au aceleai valori pentru atributele comune.
Dac cele dou relaii au schemele R1 , R2 , i A1 , A2 ,..., An , atunci
joinul natural se poate calcula prin construcia urmtoare:
R1 R2 = 1 R . A R . A and ... and R . A R . A 2
R R
1 1 2 1 1 n 2 n
63
Joinul extern stnga, notat (n acest material) prin R1 C R2 , determin o relaie nou
ce are ca atribute concatenarea atributelor din cele dou relaii, iar nregistrrile se obin
astfel: se iau nregistrrile care se obin prin joinul condiional R1 c R2, la care se
adaug nregistrrile din R1 care nu s-au folosit la acest join condiional combinate cu
valoarea null pentru toate atributele corespunztoare relaiei R2.
Joinul extern dreapta, notat prin R1 C R2 , se obine asemntor ca joinul extern
stnga, dar la nregistrrile din R1 c R2 se adaug nregistrrile din R2 care nu s-au
folosit la acest join condiional combinate cu valoarea null pentru toate atributele
corespunztoare relaiei R1.
Ctul pleac de la dou relaii R1 , R2 , R1
cu , i se noteaz prin R1 R2 .
Deducem c atributele din ct sunt date de
mulimea . O nregistrare r R1 R2 r1 r2 r
dac r2 R2 , r1 R1 ce ndeplinete r2
condiiile:
1. (r1 ) r ; r2
R2
2. (r1 ) r2 .
Semnificaia relaiei ct se vede i din figura
alturat. O nregistrare r1 aparine ctului dac
n relaia R1 apar toate concatenrile dintre
aceast nregistrare i fiecare nregistrare din R2.
O problem important legat de operatorii descrii mai sus const n determinarea unei
submulimi independente de operatori. O mulime M de operatori este independent dac
eliminnd un operator oarecare op din M se diminueaz puterea mulimii, adic va exista o
relaie obinut cu operatori din M i care nu se poate obine cu operatori din mulimea
M - {op}.
Pentru limbajul de interogare descris mai sus, o mulime independent este format din
submulimea: , , , , . Ceilali operatori se obin dup regulile urmtoare (unele
expresii au fost deja deduse mai sus):
R1 R2 R1 ( R1 R2 ) ;
R1 c R2= c ( R1 R2 ) ;
R1 , R2 , i A1 , A2 ,..., An , atunci
R1 R2 = R1
R . A R . A and ... and R . A R . A
R2
;
1 1 2 1 1 n 2 n
Fie R1 , R2 , i R3 = (null, ... , null), R4 = (null, ... , null).
R1 C R2 = (R1 c R2) [R1 - R 1 C R2 ] R3 .
64
R1 C R2 = (R1 c R2) R4 [R2 - R 1 C R2 ].
ce au o parte n ( R1 ) i a doua parte n R2. Din relaia astfel obinut vom elimina
pe R1 i rmn acele elemente ce au o parte n (R1) i nu au cealalt parte n
( R1 ) . De aici obinem:
R1 R2 ( R1 )
( R1 ) R2 R1 .
La lista de operatori relaionali amintii mai sus se pot aminti cteva instruciuni utile la
rezolvarea unor probleme:
Atribuirea: unei variabile (relaii) R i vom atribui o relaie dat printr-o expresie
construit cu operatorii de mai sus. In instruciune se poate preciza, pentru R, i
denumirea coloanelor.
R[lista] := expresie
Eliminarea duplicrilor unei relaii: (R)
Sortarea nregistrrilor dintr-o relaie: s{lista} (R)
Gruparea: {lista 1} group by {lista 2} ( R ) , care este o extensie pentru proiecie. Inregistrrile din R
sunt grupate dup coloanele din lista2, iar pentru un grup de nregistrri cu aceleai valori
pentru lista2 se evalueaz lista1 (unde pot apare funcii de grupare).
Pentru gestiunea bazelor de date relaionale s-a construit limbajul SOL (Structured
Query Language), ce permite gestiunea componentelor unei baze de date (tabel, index,
utilizator, procedur memorat, etc.).
Scurt istoric:
1970 - E.F. Codd formalizeaz modelul relaional
1974 - la IBM (din San Jose) se definete limbajul SEQUEL (Structured English
Query Language)
1975 - se definete limbajul SQUARE (Specifying Queries as Relational
Expressions).
1976 - la IBM se definete o versiune modificat a limbajului SEQUEL, cu numele
SEQUEL/2. Dup o revizuire devine SQL
1986 - SQL devine standard ANSI (American National Standards Institute)
1987 - SQL este adoptat de ISO (International Standards Organization)
1989 - se public extensia SQL89 sau SQL1
1992 - se face o revizuire i se obine SQL2 sau SQL92
65
1999 - se complecteaz SQL cu posibiliti de gestiune orientate obiect, rezultnd
SQL3 (sau SQL1999)
2003 - se adaug noi tipuri de date noi funcii, rezultnd SQL2003.
Comanda SELECT este folosit pentru interogarea bazelor de date (obinerea de
informaii). Aceast comand este cea mai complex din cadrul sistemelor ce conin comenzi
SQL. Comanda permite obinerea de date din mai multe surse de date. Ea are, printre altele,
funciile de selectie, proiectie, produs cartezian, join i reuniune, intersecie i diferen din
limbajul de interogare a bazelor de date relaionale bazat pe algebra relaiilor. Sintaxa
comenzii este dat n continuare.
ALL
*
SELECT DISTINCT
exp AS cmp exp AS cmp ...
TOP n [PERCENT]
FROM sursa1 [alias] [,sursa2 [alias]]...
[WHERE condiie]
[GROUP BY lista_cmpuri [HAVING condiie]]
UNION [ALL]
[ INTERSECT comanda_SE LECT
EXCEPT
cmp ASC cmp ASC
ORDER BY , ORDER BY ...
nrcmpDESC nrcmpDESC
Aceast comand selecteaz date din sursele de date precizate n clauza FROM. Pentru
precizarea (calificarea) cmpurilor (dac este necesar, deci dac folosirea numai a numelui
cmpului produce ambiguitate, adic exist mai multe cmpuri cu acest nume n sursele de
date) se poate folosi numele tabelului sau un nume sinonim (alias local numai n comanda
SELECT) stabilit n FROM dup numele sursei de date. Dac se definete un "alias", atunci
calificarea se face numai cu el (nu se va mai face cu numele tabelului).
O construcie numit sursa poate fi:
1. un tabel sau view din baza de date
2. (instruciune_select)
3. expresie_join, sub forma:
sursa1 [alias] operator_join sursa2 [alias] ON condiie_legatur
(expresie_join)
O condiie_elementar de legtur dintre dou surse de date (precizate prin expresie_tabel)
este de forma:
[alias_sursa1.]cmp1 operator [alias_sursa2.]cmp2
unde operator poate fi: =, <>, >, >=, <, <=. Cei doi termeni ai comparatiei trebuie s aparin
la tabele diferite.
Condiiile de legtur dintre dou surse de date sunt de forma:
cond_elementara [AND cond_elementara] ...
(condiie)
66
O expresie join are ca rezultat un tabel i este de forma:
INNER
LEFT [OUTER]
Sursa1
RIGHT [OUTER]
JOIN Sursa2 ON conditie
FULL [OUTER]
Joinul condiional, din algebra relaional, notat prin Sursa1 c Sursa2, este precizat
prin Sursa1 INNER JOIN sursa2 ON condiie, i determin acele nregistrri din produsul
cartezian al celor dou surse care ndeplinesc condiia din ON.
Joinul extern stnga, precizat prin Sursa1 LEFT [OUTER] JOIN sursa2 ON
condiie, determin o surs nou ce are ca atribute concatenarea atributelor din cele dou
surse, iar nregistrrile se obin astfel: se iau nregistrrile care se obin prin joinul condiional
Sursa1 c Sursa2, la care se adaug nregistrrile din sursa1 care nu s-au folosit la acest join
condiional combinate cu valoarea null pentru toate atributele corespunztoare din Sursa2.
Joinul extern dreapta, precizat prin Sursa1 RIGHT [OUTER] JOIN sursa2 ON
condiie, determin o surs nou ce are ca atribute concatenarea atributelor din cele dou
surse, iar nregistrrile se obin astfel: se iau nregistrrile care se obin prin joinul condiional
Sursa1 c Sursa2, la care se adaug nregistrrile din sursa2 care nu s-au folosit la acest join
condiional combinate cu valoarea null pentru toate atributele corespunztoare din Sursa1.
Joinul extern total, precizat prin Sursa1 FULL [OUTER] JOIN sursa2 ON condiie,
se obine prin reuniunea rezultatelor obinute de joinul extern stnga i joinul extern dreapta.
Alte tipuri de expresii join:
Sursa1 JOIN Sursa2 USING (lista_coloane)
Sursa1 NATURAL JOIN Sursa2
Sursa1 CROSS JOIN Sursa2
Dac n clauza FROM apar mai multe surse de date (care se vor evalua la un tabel),
atunci ntre un astfel de tabel - pe care l vom numi tabel principal, i celelalte tabele este
indicat s existe anumite legturi (stabilite prin condiii). Plecnd de la fiecare nregistrare a
tabelului principal se determin nregistrrile din celelalte tabele asociate prin astfel de
legturi (deci nregistrrile ce verific o condiie). Dac legtura (condiia) nu se stabilete,
atunci se consider c ea asociaz toate nregistrrile din celelalte tabele pentru fiecare
nregistrare a tabelului principal (se consider c valoarea condiiei este true atunci cnd ea
lipsete). Aceast condiie de legtur dintre sursele de date se precizeaz prin:
FROM sursa1[, sursa2] ... WHERE condiie_legtur
Folosind sursele de date din FROM i eventuala condiie de legtur (dac exist mai
multe surse de date) va rezulta un tabel_rezultat, cu coloanele obinute prin concatenarea
coloanelor din sursele de date, iar nregistrrile sunt determinate dup cum sunt explicate mai
sus.
In tabel_rezultat se pot pstra toate nregistrrile obinute din sursele de date, sau se
poate face o filtrare prin utilizarea unei condiii de filtrare. Aceasta condiie de filtrare va fi
trecut n clauza WHERE n continuarea condiiei de legtur. Cu o condiie de filtrare
condiia din WHERE este de forma:
WHERE condiie_filtrare
67
WHERE condiie_legtur AND condiie_filtrare
Condiia de filtrare din clauza WHERE poate fi construit dup urmtoarele reguli.
Condiiile elementare de filtrare pot fi de una din formele urmtoare:
expresie operator_relational expresie
expresie [NOT] BETWEEN valmin AND valmax
pentru a verifica dac valoarea unei expresii este cuprins ntre dou valori (valmin i
valmax) sau nu este cuprins ntre aceste valori (apare NOT)
cmp (NOT) LIKE ablon
Dup LIKE apare un ablon (ca un ir de caractere) ce precizeaz o mulime de valori.
In funcie de sistemul de gestiune folosit, exist un caracter n ablon ce precizeaz locul
unui singur caracter necunoscut n cmp, sau un caracter n ablon ce precizeaz un ir
neprecizat de caractere n cmp.
valoare valoare ...
expresie NOT IN
subselectie
Se verific dac valoarea expresiei apare (sau nu - cu NOT) ntr-o list de valori sau ntr-
o subselecie. O subselecie este o surs de date generat cu comanda SELECT i care
are numai un singur cmp - cu valori de acelai tip cu valorile expresiei. Aceast condiie
corespunde testului de "apartenen" al unui element la o mulime.
ALL
cmp operator_relational ANY (subselectie)
SOME
Valorile cmpului din stnga operatorului relaional i valorile singurului cmp din
subselecie trebuie s fie de acelai tip. Se obine valoarea adevrat pentru condiie dac
valoarea din partea stng este n relaia dat de operator pentru:
o toate valorile din subselectie (apare ALL),
o cel puin o valoare din subselectie (apare ANY sau SOME).
Condiii echivalente:
"expresie IN (subselecie)" echivalent cu "expresie = ANY (subselecie)"
"expresie NOT IN (subselecie)" echivalent cu "expresie <> ALL (subselecie)"
[NOT] EXISTS (subselectie)
Cu EXISTS se obtine valoarea adevrat dac n subselecie exist cel puin o
nregistrare, i fals dac subselecia este vid. Prezena lui NOT inverseaz valorile de
adevr.
O condiie de filtrare poate fi:
o condiie elementar
o (condiie)
o not condiie
o condiie1 and condiie2
o condiie1 or condiie2
68
O condiie elementar poate avea una din valorile: true, false, null. Valoarea null se
obine dac unul din operanzii utilizai are valoarea null. Valorile de adevr pentru operatorii
not, and, or sunt date n continuare:
true false null
not false true null
69
numerice precizate i din aceste valori se determin valoarea medie.
*
COUNT ALL
cmp
DISTINCT
Aceast funcie determin numrul de nregistrri din grup (apare '*'), numrul de valori
ale unui cmp (apare ALL, identic cu '*'), sau numrul de nregistrri distincte din grup
(cu DISTINCT).
ALL
SUM cmp sau SUM([ALL]) expresie)
DISTINCT
Pentru nregistrrile din grup se face suma valorilor unui cmp sau ale unei expresii
numerice (deci numrul de termeni este dat de numrul de nregistrri din grup) sau suma
valorilor distincte ale cmpului.
70
2.4. Probleme propuse
I.
a. Se cere o baz de date relaional, cu tabele n 3NF, ce gestioneaz urmtoarele
informaii dintr-o firm de soft:
activiti: cod activitate, descriere, tip activitate;
angajai: cod angajat, nume, list activiti, echipa din care face parte, liderul
echipei;
unde:
o activitate este identificat prin "cod activitate";
un angajat este identificat prin "cod angajat";
un angajat face parte dintr-o singur echip, iar echipa are un lider, care la rndul
su este angajat al firmei;
un angajat poate s participe la realizarea mai multor activiti, iar la o activitate
pot s participe mai muli angajai;
Justificai c tabelele obinute sunt n 3NF.
b. Pentru baza de date de la punctul a, s se rezolve, folosind algebra relaional sau
Select-SQL, urmtoarele interogri:
b1. Numele angajailor care lucreaz la cel puin o activitate de tipul "Proiectare" i
nu lucreaz la nici o activitate de tipul "Testare".
b2. Numele angajailor care sunt liderii unei echipe cu cel puin 10 angajai.
II.
a. Se cere o baz de date relaional, cu tabele n 3NF, ce gestioneaz urmtoarele
informaii dintr-o facultate:
discipline: cod, denumire, numr credite, lista studenilor care au dat examen;
studenti: cod, nume, data naterii, grupa, anul de studiu, specializarea, lista
disciplinelor la care a dat examene (inclusiv data examenului i nota obinut).
Justificai c tabelele sunt n 3NF.
b. Pentru baza de date de la punctul a, folosind algebra relaional i instructiuni
SELECT-SQL, se cer studenii (nume, grupa, nr.discipline promovate) ce au promovat
n anul 2013 peste 5 discipline. Dac un student are la o disciplin mai multe examene
cu note de promovare, atunci disciplina se numr o singur dat.
III.
a. Se cere o baz de date relaional, cu tabele n 3NF, ce gestioneaz urmtoarele
informaii despre absolvenii nscrii pentru examnul de licen: Nr.matricol, Cod i
denumire secie absolvit, Titlul lucrrii, Cod i nume cadru didactic ndrumtor, Cod
i denumire departament de care aparine cadrul didactic ndrumtor, Lista de resurse
soft necesare pentru susinerea lucrrii (ex. C#.Net, C++, etc.), Lista de resurse hard
necesare pentru susinerea lucrrii (Ram 8Gb, DVD Reader, etc.). Justificai c tabelele
sunt n 3NF.
b. Pentru baza de date de la punctul a, folosind algebra relaional i instructiuni
SELECT-SQL cel puin o dat fiecare, se cer urmtoarele liste (explicai modul de
obinere a listelor):
i. Absolvenii (nume, titlu lucrare, nume cadru didactic) pentru care cadrul
didactic ndrumtor aparine de un departament dat prin denumire.
ii. Pentru departament se cere numrul de absolveni care au cadrul didactic
ndrumtor de la acest departament.
iii. Cadrele didactice care nu au ndrumat absolveni la lucrarea de licen.
iv. Numele absolvenilor care au nevoie de urmatoarele dou resurse soft: Oracle i
C#.
71
3. Sisteme de operare
3.1. Structura sistemelor de fiiere Unix
Un sistem de fiiere Unix este gzduit fie pe un periferic oarecare (hard-disc, CD, dischet
etc.), fie pe o partiie a unui hard-disc. Partiionarea unui hard-disc este o operaie
(relativ) independent de sistemul de operare ce va fi gzduit n partiia respectiv. De
aceea, att partiilor, ct i suporturilor fizice reale le vom spune generic, discuri Unix.
Un fiier Unix este o succesiune de octei, fiecare octet putnd fi adresat n mod individual.
Este permis att accesul secvenial, ct i cel direct. Unitatea de schimb dintre disc i
memorie este blocul. La sistemele mai vechi acesta are 512 octei, iar la cele mai noi pn la
4Ko, pentru o mai eficient gestiune a spaiului. Un sistem de fiiere Unix este o structur de
date rezident pe disc. Aa dup cum se vede din figura de mai sus, un disc este compus din
patru categorii de blocuri.
Blocul 0 conine programul de ncrcare al SO. Acest program este dependent de maina sub
care se lucreaz.
Blocul 1 este numit i superbloc. In el sunt trecute o serie de informaii prin care se
definete sistemul de fiiere de pe disc. Printre aceste informaii amintim:
-numrul n de inoduri (detaliem imediat);
-numrul de zone definite pe disc;
-pointeri spre harta de bii a alocrii inodurilor;
-pointeri spre harta de bii a spaiului liber disc;
-dimensiunile zonelor disc etc.
Blocurile 2 la n, unde n este o constant a formatrii discului. Un inod (sau i-nod) este
numele, n terminologia Unix, a descriptorului unui fiier. Inodurile sunt memorate pe
disc sub forma unei liste (numiti-list). Numrul de ordine al unui inod n cadrul i-listei se
72
reprezint pe doi octei i se numete i-numr. Acest i-numr constituie legtura dintre fiier
i programele utilizator.
Partea cea mai mare a discului este rezervat zonei fiierelor. Alocarea spaiului pentru fiiere
se face printr-o variant elegant de indexare. Informaiile de plecare pentru alocare sunt
fixate n inoduri.
Structura unei intrri ntr-un fiier director este ilustrat n figura de mai jos
Deci, n director se afl numele fiierului i referina spre inodul descriptor al fiierului.
Un inod are, de regul, ntre 64 i 128 de octei i el conine informaiile din tabelul urmtor:
Fiecare sistem de fiiere Unix are cteva constante proprii, printre care amintim:
lungimea unui bloc, lungimea unui inod, lungimea unei adrese disc, cte adrese de prime
blocuri se nregistreaz direct n inod i cte referine se trec n lista de referine indirecte.
Indiferent de valorile acestor constante, principiile de nregistrare / regsire sunt aceleai.
Pentru fixarea ideilor, vom alege aceste constante cu valorile ntlnite mai frecvent la
sistemele de fiiere deja consacrate. Astfel, vom presupune c un bloc are lungimea de 512
octei. O adres disc se reprezint pe 4 octei, deci ntr-un bloc se pot nregistra 128 astfel de
se adrese. In inod trec direct primele 10 adrese de blocuri, iar lista de adrese indirecte are
3 elemente. Cu aceste constante, n figura de mai jos este prezentat structura pointerilor
spre blocurile ataate unui fiier Unix.
73
Structura unui inod i accesul la blocurile unui fiier
In inodul fiierului se afl o list cu 13 intrri, care desemneaz blocurile fizice aparinnd
fiierului.
Primele 10 intrri conin adresele primelor 10 blocuri de cte 512 octei care aparin
fiierului.
Intrarea nr. 11 conine adresa unui bloc, numit bloc de indirectare simpl. El conine
adresele urmtoarelor 128 blocuri de cte 512 octei, care aparin fiierului.
Intrarea nr. 12 conine adresa unui bloc, numit bloc de indirectare dubl. El conine adresele
a 128 blocuri de indirectare simpl, care la rndul lor conin, fiecare, adresele a cte 128
blocuri, de 512 octei fiecare, cu informaii aparinnd fiierului.
Intrarea nr. 13 conine adresa unui bloc, numit bloc de indirectare tripl. In acest
bloc sunt coninute adresele a 128 blocuri de indirectare dubl, fiecare dintre acestea
coninnd adresele a cte 128 blocuri de indirectare simpl, iar fiecare dintre acestea
conine adresele a cte 128 blocuri, de cte 512 octei, cu informaii ale fiierului.
In figura de mai sus am ilustrat prin cercuri blocurile de informaie care aparin
fiierului, iar prin dreptunghiuri blocurile de referine, n interiorul acestora marcnd
referinele. Numrul de accese necesare pentru a obine direct un octet oarecare este cel mult
4. Pentru fiiere mici acest numr este i mai mic. Att timp ct fiierul este deschis,
inodul lui lui este prezent n memoria intern. Tabelul urmtor d numrul maxim de accese
la disc pentru a obine, n acces direct orice octet dintr-un fiier, n funcie de lungimea
fiierului.
74
La sistemele Unix actuale lungimea unui bloc este de 4096 octei care poate nregistra 1024
adrese, iar n inod se nregistreaz direct adresele primelor 12 blocuri. In aceste
condiii, tabelul de mai sus se transform n:
In cadrul unui sistem de fiiere, apelurile sistem Unix gestioneaz opt tipuri de fiiere i
anume:
1. Normale (obinuite)
2. Directori
3. Legturi hard (hard links)
4. Legturi simbolice (symbolic links)
5. Socketuri (sockets)
6. FIFO - pipe cu nume (named pipes)
7. Periferice caracter
8. Periferice bloc
Pe lng aceste opt tipuri, mai exist nc patru entiti, pe care apelurile sistem le vd, din
punct de vedere sintactic, tot ca i fiiere. Aceste entiti sunt gestionate de nucleul Unix, au
suportul fizic tot n nucleu i folosite la comunicri ntre procese. Aceste entiti sunt:
9. Pipe (anonymous pipes)
10. Segmente de memorie partajat
11. Cozi de mesaje
12. Semafoare
Fiierele obinuite sunt privite ca iruri de octei, accesul la un octet putndu-se face fie
secvenial, fie direct prin numrul de ordine al octetului.
75
Fiierele directori. Un fiier director se deosebete de un fiier obinuit numai prin informaia
coninut n el. Un director conine lista de nume i adrese pentru fiierele subordonate lui.
Uzual, fiecare utilizator are un director propriu care puncteaz la fiierele lui obinuite, sau la
ali subdirectori definii de el.
Fiierele speciale. In aceast categorie putem include, pentru moment, ultimele 6 tipuri de
fiiere. In particular, Unix privete fiecare dispozitiv de I/O ca i un fiier de tip special. Din
punct de vedere al utilizatorului, nu exist nici o deosebire ntre lucrul cu un fiier disc
normal i lucrul cu un fiier special.
In mod obinuit, fiecare utilizator folosete un director curent, ataat utilizatorului la intrarea
n sistem. Utilizatorul poate s-i schimbe acest director (cd), poate crea un nou director
subordonat celui curent, (mkdir), s tearg un director (rmdir), s afieze calea de acces
de la root la un director sau fiier (pwd) etc.
Apariia unui mare numr de distribuitori de Unix a condus, inevitabil, la proliferarea unui
numr oarecare de "sisteme de fiiere extinse" proprii acestor distribuitori. De exemplu:
Solaris utilizeaz sistemul de fiiere ufs;
Linux utilizeaz cu precdere sistemul de fiiere ext2 i mai nou, ext3;
IRIX utilizeaz xfs
etc.
Actualele distribuii de Unix permit utilizatea unor sisteme de fiiere proprii altor sisteme de
operare. Printre cele mai importante amintim:
Sistemele FAT i FAT32 de sub MS-DOS i Windows 9x;
Sistemul NTFS propriu Windows NT i 2000.
Din fericire, aceste extinderi sunt transparente pentru utilizatorii obinuii. Totui, se
recomand pruden atunci cnd se efectueaz altfel de operaii dect citirea din fiierele
create sub alte sisteme de operare dect sistemul curent. De exemplu, modificarea sub Unix a
unui octet ntr-un fiier de tip doc creat cu Word sub Windows poate uor s compromit
fiierul aa nct el s nu mai poat fi exploatat sub Windows!
Principiul structurii arborescente de fiiere este acela c orice fiier sau director are un singur
printe. Automat, pentru fiecare director sau fiier exist o singur cale (path) de la rdcin
la directorul curent. Legtura ntre un director sau fiier i printe o vom numi legtur
natural. Evident ea se creeaz odat cu crearea directorului sau fiierului respectiv.
76
3.1.2.1. Legturi hard i legturi simbolice
In anumite situaii este util partajarea unei poriuni a structurii de fiiere ntre mai muli
utilizatori. De exemplu, o baz de date dintr-o parte a structurii de fiiere trebuie s fie
accesibil mai multor utilizatori. Unix permite o astfel de partajare prin intermediul
legturilor suplimentare. O legtur suplimentar permite referirea la un fiier pe alte ci
dect pe cea natural. Legturile suplimentare sunt de dou feluri: legturi hard i legturi
simbolice (soft).
Legturile hard sunt identice cu legturile naturale i ele pot fi create numai de ctre
administratorul sistemului. O astfel de legtur este o intrare ntr-un director care puncteaz
spre o substructur din sistemul de fiiere spre care puncteaz deja legtura lui natural. Prin
aceasta, substructura este vzut ca fiind descendent din dou directoare diferite! Deci,
printr-o astfel de legtur un fiier primete efectiv dou nume. Din aceast cauz, la
parcurgerea unei structuri arborescente, fiierele punctate prin legturi hard apar duplicate.
Fiecare duplicat apare cu numrul de legturi ctre el.
atunci n sistemul de fiiere se vor vedea dou fiiere identice: vechi si linknou, fiecare
dintre ele avnd marcat faptul c sunt dou legturi spre el.
Legturile hard pot fi fcute numai n interiorul aceluiai sistem de fiiere (detalii puin mai
trziu).
Legturile simbolice sunt intrri speciale ntr-un director, care puncteaz (refer) un fiier
(sau director) oarecare n structura de directori. Aceast intrare se comport ca i un
subdirector al directorului n care s-a creat intrarea.
ln - s caleInStructuraDeDirectori numeSimbolic
77
/:
A B C . A
-
A: D E G . A
.. B: D E F . A
.. C:
D: E: F G . .. D: E G . .. E: F:
C:
F: G: G:
Structura arborescent mpreun cu legturile simbolice sau hard confer sistemului de fiiere
Unix o structur de graf aciclic. In exemplul din figura de mai sus este prezentat un exemplu
simplu de structur de fiiere. Prin literele mari A, B, C, D, E, F, G am indicat nume de
fiiere obinuite, nume de directori i nume de legturi. Este evident posibil ca acelai nume
s apar de mai multe ori n structura de directori, graie structurii de directori care elimin
ambiguitile. Fiierele obinuite sunt reprezentate prin cercuri, iar fiierele directori prin
dreptunghiuri.
In exemplul de mai sus exist 12 noduri - fiiere obinuite sau directori. Privit ca un arbore,
deci considernd numai legturile naturale, el are 7 ramuri i 4 nivele.
S presupunem c cele dou legturi (desenate cu linie punctata) sunt simbolice. Pentru
comoditate, vom nota legtura simbolic cu ultima liter din specificarea cii. Crearea celor
dou legturi se poate face, de exemplu, prin succesiunea de comenzi:
cd /A
ln -s /A/B/D/G G Prima legtur
cd /A/B/D
ln -s /A/E E A doua legtur
S presupunem acum c directorul curent este B. Vom parcurge arborele n ordinea director
urmat de subordonaii lui de la stnga spre dreapta. Urmtoarele 12 linii indic toate cele 12
noduri din structur. Pe aceeai linie apar, atunci cnd este posibil, mai multe specificri ale
aceluiai nod. Specificrile care fac uz de legturi simbolice sunt subliniate. Cele mai lungi 7
ramuri vor fi marcate cu un simbol # n partea dreapt.
78
/ ..
/A ../A
/A/D ../A/D #
/A/E ../A/E D/E ./D/E
/A/E/F ../A/E/F D/E/F ./D/E/F #
/A/E/G ../A/E/G D/E/G ./D/E/G #
/B .
/B/D D ./D
/B/D/G D/G ./D/G /A/G ../A/G #
/B/E E ./E #
/B/F F ./F #
/C ../C #
rm D/G
rm /A/G
Este clar c fiierul trebuie s rmn activ dac este ters numai de ctre una dintre
specificri.
Pentru aceasta, n descriptorul fiierului respectiv exist un cmp numit contor de legare.
Acesta are valoarea 1 la crearea fiierului i crete cu 1 la fiecare nou legtur. La tergere,
se radiaz legtura din directorul printe care a cerut tergerea, iar contorul de legare scade cu
1. Abia dac acest contor a ajuns la zero, fiierul va fi efectiv ters de pe disc i blocurile
ocupate de el vor fi eliberate.
Procese Unix: creare, funciile fork, exec, exit, wait; comunicare prin pipe i FIFO
In seciunile din acest subcapitol vom prezenta cele mai importante apeluri sistem pentru
lucrul cu procese: fork, exit, wait i exec*. Incepem cu fork(), apelul sistem
pentru crearea unui proces.
In sistemul de operare Unix un proces se creeaz prin apelul funciei sistem fork(). La o
funcionare normal, efectul acesteia este urmtorul: se copiaz imaginea procesului ntr-o
zon de memorie liber, aceast copie fiind noul proces creat, n prima faz identic cu
procesul iniial. Cele dou procese i continu execuia n paralel cu instruciunea care
urmeaz apelului fork.
79
Procesul nou creat poart numele de proces fiu, iar procesul care a apelat funcia fork() se
numete proces printe. Exceptnd faptul c au spaii de adrese diferite, procesul fiu difer de
procesul printe doar prin identificatorul su de proces PID, prin identificatorul procesului
printe PPID i prin valoarea returnat de apelul fork(). La derulare normal, un apel
fork() ntoarce n procesul printe (procesul care a lansat apelul fork()) pid-ul noului
proces fiu, iar n procesul fiu, ntoarce valoarea 0.
fork() fork()
>0 (pid fiu)
Proces fiu
Context
0
fork()
In figura de mai sus am ilustrat acest mecanism, unde sgeile indic instruciunea care se
execut n mod curent n proces.
In caz de eec, fork ntoarce valoarea 1 i desigur, seteaz corespunztor variabila errno.
Eecul apelului fork poate s apar dac:
nu exist memorie suficient pentru efectuarea copiei imaginii procesului printe;
numrul total de procese depeste o limit maxim admis.
Acest comportament al lui fork permite descrierea uoar a dou secvene de instruciuni
care s se deruleze n paralel, sub forma:
if ( fork() == 0 )
{ - - - instruciuni ale procesului fiu - - - }
else
{ - - - instruciuni ale procesului tat - - - }
main(){
int pid,i;
printf("\nInceputul programului:\n");
if ((pid=fork())<0) err_sys("Nu pot face fork()\n");
else if (pid==0){//Suntem in fiu
for (i=1;i<=10;i++){
80
sleep(2); //dormim 2 secunde
printf("\tFIUL(%d) al PARINTELUI(%d):3*%d=%d\n",
getpid(),getppid(),i,3*i);
}
printf("Sfarsit FIU\n");
}
else if (pid>0){//Suntem in parinte
printf("Am creat FIUL(%d)\n",pid);
for (i=1;i<=10;i++){
sleep(1); //dormim 1 secunda
printf("PARINTELE(%d):2*%d=%d\n",getpid(),i,2*i);
}
printf("Sfarsit PARINTE\n");
}
}
In mod intenionat, am fcut astfel nct procesul fiu s atepte mai mult dect printele (n
cazul calculelor complexe apar adesea situaii n care operaiile unuia dintre procese dureaz
mai mult n timp). Ca urmare, printele va termina mai repede execuia. Rezultatele obinute
sunt:
Inceputul programului:
Am creat FIUL(20429)
PARINTELE(20428): 2*1=2
FIUL(20429) al PARINTELUI(20428): 3*1=3
PARINTELE(20428): 2*2=4
PARINTELE(20428): 2*3=6
FIUL(20429) al PARINTELUI(20428): 3*2=6
PARINTELE(20428): 2*4=8
PARINTELE(20428): 2*5=10
FIUL(20429) al PARINTELUI(20428): 3*3=9
PARINTELE(20428): 2*6=12
PARINTELE(20428): 2*7=14
FIUL(20429) al PARINTELUI(20428): 3*4=12
PARINTELE(20428): 2*8=16
PARINTELE(20428): 2*9=18
FIUL(20429) al PARINTELUI(20428): 3*5=15
PARINTELE(20428): 2*10=20
Sfarsit PARINTE
FIUL(20429) al PARINTELUI(1): 3*6=18
FIUL(20429) al PARINTELUI(1): 3*7=21
FIUL(20429) al PARINTELUI(1): 3*8=24
FIUL(20429) al PARINTELUI(1): 3*9=27
FIUL(20429) al PARINTELUI(1): 3*10=30
Sfarsit FIU
Aproape toate sistemele de operare i toate mediile de programare ofer, ntr-un fel sau altul,
mecanisme de lansare a unui program din interiorul altuia. Unix ofer acest mecanism prin
intermediul familiei de apeluri sistem exec*. Dup cum se va vedea, utilizarea combinat de
fork exec ofer o mare elasticitate manevrrii proceselor.
Apelurile sistem din familia exec lanseaz un nou program n cadrul aceluiai proces.
Apelului exec i se furnizeaz numele unui fiier executabil, iar coninutul acestuia se
suprapune peste programul procesului existent, aa cum se vede n Figura 3.2.
81
Inainte de exec: Dupa exec:
Context Context
vechi vechi
Context
nou
nou
ogram
pr
exec()
In urma lui exec instruciunile aflate n programul curent nu se mai execut, n locul lor se
lanseaz instruciunile noului program.
Unix ofer, n funcie de trei criterii, ase astfel de apeluri. Criteriile sunt:
Specificarea cii spre programul executabil ce va fi lansat: absolut sau relativ la
directoarele indicate prin variabila de mediu PATH.
Mediul este motenit sau se creeaz un nou mediu.
Specificarea argumentelor din linia de comand se face printr-o list explicit sau printr-
un vector de pointeri spre aceste argumente.
Din cele opt posibile, s-au eliminat cele dou cu cale relativ i mediu nou. Prototipurile celor
ase apeluri exec*() sunt:
82
Apelul sistem:
exit(int n)
provoac terminarea procesului curent i revenirea la procesul printe (cel care l-a creat prin
fork). Intregul n precizeaz codul de retur cu care se termin procesul. In cazul n care
procesul printe nu mai exist, procesul este trecul n starea zombie i este subordonat
automat procesului special init (care are PID-ul 1).
Ateptarea terminrii unui proces se realizeaz folosind unul dintre apelurile sistem wait()
sau waitpid(). Prototipurile acestora sunt:
Apelul wait() suspend execuia programului pn la terminarea unui proces fiu. Dac fiul
s-a terminat nainte de apelul wait(), apelul se termin imediat. La terminare, toate
resursele ocupate de procesul fiu sunt eliberate.
Conceptul a aprut prima dat sub Unix, pentru a permite unui proces fiu s comunice cu
printele su. De obicei procesul printe redirecteaz ieirea sa standard, stdout, ctre un
pipe, iar procesul fiu i redirecteaz intrarea standard, stdin, din acelai pipe. In
majoritatea sistemelor de operare se folosete operatorul | pentru specificarea acestui gen
de conexiuni ntre comenzi ale sistemului de operare.
Un pipe Unix este un flux unidirecional de date, gestionat de ctre nucleul sistemului. De
fapt, n nucleu se rezerv un buffer de minimum 4096 octei n care octeii sunt gestionai aa
cum am descris mai sus. Crearea unui pipe se face prin apelul sistem:
Intregul fd[0] se comport ca un ntreg care identific descriptorul pentru citirea din
"fiierul" pipe, iar fd[1] ca un ntreg care indic descriptorul pentru scriere n pipe. In urma
crerii, legtura user nucleu prin acest pipe apare ca n figura de mai jos.
83
read fd[0]
proces user:
write fd[1]
pipe
nucleu:
sensul datelor
Evident, un pipe ntr-un singur proces nu are sens. Este ns esenial funcionarea lui pipe
combinat cu fork. Astfel, dac dup crearea lui pipe se execut un fork, atunci legtura
celor dou procese cu pipe din nucleu apare ca n figura urmtoare.
Nucleu
pipe
Sensul datelor
Natural, dac se dorete ordinea invers, atunci se vor executa operaiile close(fd[1]) n
procesul printe i close(fd[0]) n procesul fiu.
84
$ who | sort
Noi vom prezenta realizarea conexiunii ntre cele dou comenzi: who | sort prin pipe.
Procesul printe (care nlocuiete procesul shell) genereaz doi fii, iar acetia i redirecteaz
corespunztor intrrile / ieirile. Primul dintre ele execut who, cellalt sort, iar printele
ateapt terminarea lor. Sursa este prezentat n programul de mai jos.
//whoSort.c
//Lanseaza in pipe comenzile shell: $ who | sort
#include <unistd.h>
main (){
int p[2];
pipe (p);
if (fork () == 0) { // Primul fiu
dup2 (p[1], 1); //redirectarea iesirii standard
close (p[0]);
execlp ("who", "who", 0);
}
else if (fork () == 0) { // Al doilea fiu
dup2 (p[0], 0); // redirectarea intrarii standard
close (p[1]);
execlp ("sort", "sort", 0);//executie sort
}
else { // Parinte
close (p[0]);
close (p[1]);
wait (0);
wait (0);
}
}
Observatie: pentru a se nelege mai bine exemplul de mai sus, invitm cititorul s citeasc
din manualele Unix prezentarea apelului sistem dup2. Aici apelul dup2 are ca paramentru
un descriptor pipe.
Cel mai mare dezavantaj al lui pipe sub Unix este faptul c poate fi utilizat numai n
procese nrudite: procesele care comunic prin pipe trebuie s fie descendeni din procesul
creator al lui pipe. Aceasta deoarece ntregii descriptori de citire/scriere din/n pipe sunt unici
i sunt transmii proceselor fiu ca urmare a apelului fork().
In jurul anului 1985 (Unix System V), a aprut conceptul FIFO ( pipe cu nume). Acesta este
un flux de date unidirecional, accesat prin intermediul unui fiier rezident n sistemul de
fiiere. Incepnd cu Unix System V, exist fiiere de tip FIFO. Spre deosebire de pipe,
fiierele FIFO au nume i ocup un loc n sistemul de fiiere. Din aceast cauz, un FIFO
85
poate fi accesat de orice dou procese, nu neaprat cu printe comun. Atenie ns: chiar dac
un FIFO exist ca fiier n sistemul de fiiere, pe disc nu se stocheaz nici o dat care trece
prin canalul FIFO, acestea fiind stocate i gestionate n buffer-ele nucleului sistemului de
operare!
Conceptual, canalele pipe i FIFO sunt similare. Deosebirile eseniale dintre ele sunt
urmtoarele dou:
suportul pentru pipe este o poriune din memoria RAM gestionat de nucleu, n timp
ce FIFO are ca suport discul magnetic;
toate procesele care comunic prin-un pipe trebuie s fie descendente ale procesului
creator al canalului pipe, n timp ce pentru FIFO nu se cere nici o relaie ntre
procesele protagoniste.
$ mknod numeFIFO p
$ mkfifo numeFIFO
Menionm c cele dou apeluri de mai sus, dei sunt specificate de POSIX, nu sunt
amndou apeluri sistem pe toate implementrile de Unix. Astfel, pe FreeBSD sunt prezente
ambele apeluri sistem mknod() i mkfifo(), dar pe Linux i Solaris exist numai apelul
sistem mknod(), funcia de bibliotec mkfifo() fiind implementat cu ajutorul apelului
sistem mknod(). Cele dou comenzi shell sunt ns disponibile pe majoritatea
implementrilor de Unix. Sub sistemele Unix mai vechi, comenzile mknod i mkfifo sunt
permise numai super-user-ului. Incepnd cu Unix System V 4.3 ele sunt disponibile i
utilizatorului obinuit.
tergerea (distrugerea) unui FIFO se poate face fie cu comanda shell rm numeFIFO, fie cu
un apel sistem C unlink() care cere un descriptor pentru fiierul FIFO.
Odat ce FIFO este creat, el trebuie s fie deschis pentru citire sau scriere folosind apelul
sistem open. Precizarea sau nu a flagului O_NDELAY la apelul sistem open are efectele
indicate n tabelul urmtor.
86
Condiii normal setat O_NDELAY
deschide FIFO ateapt pn cnd apare un proces care revine imediat fr a
read-only, dar nu deschide FIFO pentru scriere semnala eroare
exist proces de
scriere n FIFO
deschide FIFO ateapt pn apare un proces pentru citire
revine imediat cu
write-only, dar nu semnalarea de eroare:
exist proces de variabila errno va
citire din FIFO deveni ENXIO
ateapt pn cnd apar date n pipe sau revine
citire din FIFO sau imediat, cu
din pipe, dar nu FIFO, sau pn cnd nu mai exist proces ntoarcerea valorii 0
exist date de citit
deschis pentru scriere. Intoarce numrul de
date citite dac apar noi date, sau 0 dac nu
mai exist proces de scriere
scrie n FIFO sau ateapt pn cnd se face spaiu disponibil, revine imediat, cu
pipe, dar acesta apoi scrie attea date, ct i permite spaiul ntoarcerea valorii 0
este plin disponibil
Modelul de aplicaie client / server este clasic n programare. In cele ce urmeaz vom ilustra
o schem de aplicaii client / server bazate pe comunicaii prin FIFO. Pentru a se asigura
comunicarea bidirecional se folosesc dou FIFO-uri. Pentru partea specific aplicaiei, se
folosesc metodele client(int in, int out) i server(int in, int out).
Fiecare dintre ele primete ca i parametri descriptorii de fiiere, presupuse deschise, prin
care comunic cu partenerul.
In cele dou programe care urmeaz este schiat schema serverului i a clientului. Cele dou
programe presupun c cele dou canale FIFO sunt create, respectiv terse, prin comenzi Unix.
Programul server:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stdio.h>
#include <unistd.h>
#include "server.c"
main() {
int readfd, writefd;
87
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
close (readfd);
close (writefd);
}
Programul client:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stdio.h>
#include <unistd.h>
#include "client.c"
main() {
int readfd, writefd;
- - - - - - - - - - - - - -
client(readfd, writefd);
- - - - - - - - - - - - -
close (readfd);
close (writefd);
}
88
3.3. Interpretoare ale fiierelor de comenzi
Un interpretor de comenzi (shell) este un program special care furnizeaz o interfa ntre
nucleul sistemului de operare Unix (kernel-ul) i utilizator. Din aceast perspectiv, a
asigurrii legturii ntre utilizator i sistem, un shell poate fi privit diferit:
Este important s remarcm, din algoritmul de mai sus, cele dou moduri n care o comand
poate fi executat:
modul foreground - execuie la vedere. In acest gen de execuie sh lanseaz execuia
comenzii, ateapt terminarea ei dup care afieaz din nou prompterul pentru o nou
comand. Acesta este modul implicit de execuie al oricrei comenzi Unix.
modul background - execuie n fundal, ascuns. In acest gen de execuie sh lanseaz
procesul care va executa comanda, dar nu mai ateapt terminarea ei ci afieaz
imediat prompterul, oferind utilizatorului posibilitatea de a lansa imediat o nou
89
comand. Comada care se dorete a fi lansat n background trebuie s se ncheie cu
caracterul special '&'.
Intr-o fereastr (sesiune) de lucru Unix se pot rula oricte comenzi n background i numai
una n foreground. Iat, spre exemplu, trei astfel de comenzi, dou lansate n background - o
copiere de fiier (comanda cp) i o compilare (comanda gcc) i una n foreground - editare
de fiier (comanda vi):
cp A B &
gcc x.c &
vi H
In cele ce urmeaz vom prezenta gramatica limbajului sh cel mai simplu shell de sub Unix.
Vom pune n eviden principalele categorii sintactice, semantica / funcionalitatea fiecrei
astfel de categorii se deduce uor din context.
Vom considera urmtoarele convenii, pe care le folosim doar n scrierea regulilor gramaticii:
O categorie gramatical se poate defini prin una sau mai multe alternative de definire.
Alternativele se scriu cte una pe linie, ncepnd cu linia de dup numele categoriei
gramaticale, astfel:
categorieGramatical:
alternativa_1 de definire
----
alternativa_n de definire
[ ]? Semnific faptul c, construcia dintre paranteze va aprea cel mult odat.
[ ]+ Semnific faptul c, construcia dintre paranteze va aprea cel puin odat.
[ ]* Semnific faptul c, construcia dintre paranteze poate s apar de 0 sau mai multe
ori.
O comanda sh poate avea oricare dintre cele 9 forme prezentate. Una dintre modalitile de
definire este cea de comandElementar, unde o astfel de comand elementar este un ir de
elemente, un element putnd fi definit n 10 moduri distincte. O legarePipe este fie o singur
comand, fie un ir de comenzi separate prin caracterul special '|'. In sfrit, listaCom este o
succesiune de legarePipe separate i eventual terminate cu simboluri speciale.
90
Se poate observa c, n conformitate cu gramatica de mai sus, sh accept i construcii fr
semantic! De exemplu, comand poate fi o comandElementar, care s conin un singur
element, format din >&-;. O astfel de linie este acceptat de sh, fiind corect din punct de
vedere sintactic, dei nu are sens din punct de vedere semantic.
Structurile alternative if i case sunt nchise de construciile fi, respectiv esac, obinute prin
oglindirea cuvintelor de start. In cazul ciclurilor repetitive, sfritul acestora este indicat prin
folosirea cuvntului rezervat done. Nu s-a folosit construcia similar corespunztoare lui do,
deoarece od este numele unui comenzi clasice Unix.
comand:
comandElementar
( listaCom )
{ listaCom }
if listaCom then listaCom [ elif listaCom then listaCom ]* [ else listaCom ]? fi
case cuvant in [ cuvant [ | cuvant ]* ) listaCom ;; ]+ esac
for nume do listaCom done
for nume in [ cuvant ]+ do listaCom done
while listaCom do listaCom done
until listaCom do listaCom done
comandElementar:
[ element ]+
91
listaCom:
legarePipe [ separator legarePipe ]* [ terminator ]?
legarePipe:
comanda [ | comanda ]*
element:
cuvnt
nume=cuvnt
>cuvnt
<cuvnt
>>cuvnt
<<cuvnt
>&cifra
<&cifra
<&-
>&-
separator:
&&
||
terminator
terminator:
;
&
1 for F in *.txt; do
2 K=`grep abc $F`
92
3 if [ $K != ]; then
4 echo $F
5 fi
6 done
II.
a. Se d fragmentul de cod de mai jos. Indicai liniile care se vor tipri pe ecran n ordinea n
care vor aprea, considernd c apelul sistem fork se execut cu succes? Justificai
rspunsul.
int main() {
int i;
for(i=0; i<2; i++) {
printf("%d: %d\n", getpid(), i);
if(fork() == 0) {
printf("%d: %d\n", getpid(), i);
exit(0);
}
}
for(i=0; i<2; i++) {
wait(0);
}
return 0;
}
more raport.txt
rm raport.txt
for f in *.sh; do
if [ ! -x $f ]; then
chmod 700 $f
fi
done
mail -s "Raport fisiere afectate" admin@scs.ubbcluj.ro <raport.txt
93
4. Bibliografie general
94
31. OMG. MDA Guide Version 1.0.1, 2003. http://www.omg.org/cgi-bin/doc?omg/03-06-
01.pdf
95