Documente Academic
Documente Profesional
Documente Cultură
Programare Obiectuala - Intro PDF
Programare Obiectuala - Intro PDF
Şi acum, pe cai. Vom începe prin a studia un limbaj nou, apărut în acest mileniu,
numit C# (se citeşte C sharp). Mediul de programare Microsoft Visual C# 2005 Express
Edition, în care vom lucra în cele ce urmează, oferă un cadru (wizard) pentru construirea
interfeţelor, comun mediilor de dezvoltare de programe de tip Visual al firmei Microsoft,
numit Windows Form Designer. Pentru a vedea ce ne oferă acest wizard, să deschidem un
noi proiect Visula C#, numit unu. Pentru aceasta, vom deschide mediul de programare
Microsoft Visual C# 2005 Express Edition, în caseta Recent Projects vom alege
Create: Project… . Apoi, în caseta New Project apărută, vom alege Windows
Application şi vom apăsa OK. Veţi avea în faţă o aplicaţie ca şi cea prezentată în fig. 1.
1
Figura 1. Interfaţa Form Designer
Observaţie: Daca tab-ul Toolbox lipseşte, poate fi afişat alegând din meniu View şi
respectiv Toolbox.
În partea dreaptă a imaginii, se pot vedea două panouri. În dreapta sus se găseşte
panoul Solution Explorer, în care popt fi văzute toate proiectele deschise precum şi
fişierele asociate acestora. În dreapta jos, poate fi văzut panoul Properties. Acest panou
conţine toate proprietăţile elementelor selectate, oferind posibilitatea editării acestora fără
prea mare efort.
2
înţelege structura acestora şi relaţiile dintre ele, a fost dezvoltat un limbaj special de
descriere, numit UML (Universal Modeling Language). Vom încerca în cele ce urmăază,
să introducem şi câteva noţini referitoare la acest limbaj.
Să presupunem de exemplu că
avem o clasa Prăjitură. Reprezentarea Prăjitură Savarina:Prăjitură
UML a acestei clase poate fi văzută în
fig. 2.a. Se poate observa că numele
clasei este scris în partea de sus casetei.
În mod absolut similar, se poate a) b)
reprezenta şi o instanţiere a clasei, fie Figura 2
aeasta Savarină (fig. 2.b). Numele
instanţei este prezentat împreună cu numele clasei, separate prin :.
1.2.2 Metode
O metodă este o funcţie expusă de un obiect. Metodele sunt utilizate pentru a
permite accesul la atributele obiectului. Pot fi de asemenea publice sau private. Prin
intermediul metodelor se poate verifica sau modifica starea unui obiect, inclusiv în
situaţia în care accesul se face asupra unor atribute private. În UML, metodele sunt
prezentate în caseta a treia (fig. 4).
3
În fig. 4, a fost adăugată metoda AdaugZahar, care Prăjitură
primeşte ca parametru cantitatea de zahăr adaugata sub +Faina: string
forma unui octet fără semn şi returnează noua valoare a +Drojdie: bool
atributului Zahar. În UML, se utilizează de asemenea +Lapte: bool
+Zahar: byte
identificatorii in, out şi inout pentru a specifica sensul +Altele: string
fluxului de date. +AdaugZahar(in cantitate:byte):byte
O clasă poate expune mai mulţi constructori neimpliciţi, care pot avea parametri.
Aceştia vor fi apelaţi de o manieră smilară.
1.2.4 Destructori
Destructorul este metoda care eliberează memoria calculatorului de obiectele a
căror ciclu de viaţă a expirat. Spre deosebire de C++, în C# destructorul nu trebuie
definit. Mediul de programare implementează un colector de reziduri (garbage collector),
care periodic inspectează dacă există memorie alocată unor obiecte care nu mai există şi
realizează eliberarea automată a acesteia.
1.3.1 Interfeţe
În programarea obiectuală, poate apare situaţia în care clase diferite pot avea un
număr de membri (atribute sau metode) comuni, care asigură legătura cu exteriorul. În
această situaţie, aceşti membri pot fi grupaţi împreună, constituind o interfaţă. Deci o
interfaţă este o colecţie de atribute şi metode publice, grupate împreună pentru a
încapsula o funcţionalitate specifică. O interfaţă odată definită, poate fi implementată în
interiorul unei clase.
O interfaţă nu poate exista doar ea insăşi. O interfaţă nu poate fi instanţiată şi în
plus o interfaţă nu poate implementa cod în nici unul din membrii săi.
Ca exemplu, să considerăm încă o clasa, numită Budincă, care expune aceeaşi
membri ca şi clasa Prajitură, diferind doar prin câmpurile Faina respectiv Orez. Toţi
membri comuni vor putea fi încapsulaţi în interfaţa IDulciuri.
4
Prajitura IDulciuri
<<Interface>>
IDulciuri +Faina: string
+Drojdie: bool
+Lapte: bool
+Zahar: byte Budinca IDulciuri
+Altele: string
+Orez: string
+AdaugZahar(in cantitate:byte): byte
Figura 5
O clasă poate utiliza mai multe interfeţe, iar o interfaţă poate fi comună mai
multor clase.
1.3.2 Moştenirea
Moştenirea este cea mai importantă caracteristică a programării obiectuale. Prin
intermediul ei, o clasă, numită clasă
derivată, poate moşteni trăsăturile unei alte Animal
clase, numită clasă de bază, sau superclasă,
pe care le poate extinde şi eventual
+Hraneste()
modifica. Se pot realiza astfel ierarhii de +Reproduce()
clase, fără a fi necesară redefinirea fiecări
clase în parte. Spre deosebire de C++, C#
permite doar moştenirea simplă. În figura 6
este prezentată o astfel de ierarhie de clase.
Clasa Animal este clasa de bază şi expune 2 Pasare Mamifer
metode: Hraneste() şi Reproduce(). Din
această clasă sunt derivate clasele Pasare şi +Zboara() +Fuge()
Mamifer, care moştenesc metodele clasei de +FaceOua() +Alapteaza()
bază, dar adaugă fiecare 2 metode noi:
Zboara() şi FaceOua(), respectiv Fuge() şi Figura 6
Alaptează().
Să ne reamintim accesibilitatea membrilor clasei. Membrii privaţi ai clasei de
bază nu vor fi accesibili claselor derivate, în timp ce membri publici vor fi accesibili atât
claselor derivate cât şi restului codului. Va apare un al treilea tip de acces, respectiv
protejat (protected). Un membru protejat al clasei de bază va fi accesibil claselor
derivate, dar nu va fi accesibil restului codului.
Oricare dintre membrii clasei poate fi definit ca virtual, adică poate fi redefinit
(supraânscris) în clasele derivate. Cu alte cuvinte, un membru virtual poate furniza o
implementare alternativă în clasele derivate. Această implementare alternativă nu şterge
implementarea originală din clasa de bază, care rămâne accesibilă claselor derivate, dar o
ascunde pentru restul codului. Un membru virtual nu poate fi privat, pentru că în acest
caz nu ar fi accesibil claselor derivate pentru a fi redefinit. Dacă un membru este virtual,
el va apare în diagrama UML a clasei derivate, având evident o altă implementare.
Metoda Hraneste() este implementată virtual în clasa Mamifer (fig. 7).
5
Există situaţii în care anumite metode virtuale ale clasei de bază nu pot fi
implementate. Ele trebuie declarate, pentru a fi redefinite în clasele derivate. Astfel de
metode se numesc virtuale pure. O clasă care conţine cel puţin o metodă virtuală pură
este o clasă abstractă. O clasă abstractă nu poate fi instanţiată, deci nu se pot defini
obiecte de acel tip. În UML numele unei clase abstracte se reprezintă prin caractere
italice.
O clasă poate fi sigilată (sealed). O
astfel de clasă nu poate fi folosită ca şi clasă Animal
de bază, sau cu alte cuvinte, din ea nu se pot
deriva alte clase.
+Hraneste()
Ierahia de clase este pusă în evidenţă +Reproduce()
în numele unei clase prin specificarea
numelui tuturor claselor din amonte,
separate prin punct. Vom avea astfel, în
exemplul nostru Animal.Pasare, respectiv
Animal.Mamifer, ş.a.m.d. Pasare Mamifer
1.3.3 Polimorfismul
+Zboara() +Fuge()
Ca o consecinţă a moştenirii, clasele +FaceOua() +Alapteaza()
+Hraneste()
de bază şi clasele derivate pot realiza o
suprapunere a metodelor şi proprietăţilor pe
Figura 7
care le expun. Atfel, obiecte instanţiate din
clase având aceeaşi clasă de bază, pot fi
tratate în comun. De exmplu, cum clasa Animal expune metoda Hraneste(), aceasta va
putea fi expusă prin aceeaşi sintaxă de obiecte ale claselor derivate:
Mai mult, prin intermediul polumorfismului, vom putea atribui unui obiect al
clasei de bază, un obiect al unei clase derivate, fără a fi nevoie de nici o transformare
explicită de tip:
Animal unanimal = pisica;
unanimal.Hraneste();
6
1.3.4 Relaţii între obiecte
Moştenirea reprezintă o relaţie simplă între obiecte, în care membrii unei clase de
bază sunt expuşi complet de obiecte aparţinând unor clase derivate. Există însă şi alte
situaţii în care relaţiile între obiecte devin importante:
• Continut (containement) – situaţie în care o clasă conţine altă clasă. Este oarecum
sismilară moştenirii, dar permite clasei care conţine să controleze accesul la membrii
clasei conţinute şi chiar să execute acţiuni suplimentare înaninte de a permite
utilizarea membrilor clasei conţinute.
• Colecţie (collection) – situaţie în care o clasă se manifestă ca şi container pentru
instabţe multiple a altei clase. Este similar cu a avea tablouri unidimensionale de
obiecte, asupra cărora se pot executa indexări, sortări, etc.
1.3.5 Conţinerea
Conţinerea este simplu de realizat prin declararea unui obiect ca şi câmp al unei
clase. Acest câmp trebuie să fie public, pentru
ca utilizatorii obiectului conţinut să aibă acces Alapteaza
1
la metodele şi proprietăţile expuse de acesta.
Ca alternativă, acest câmp poate fi
+Lapte()
privat. Astfel, nici unul din membrii clasei din
care face parte acel obiect nu va fi accesibil.
Accesul la aceştia se va putea face doar prin
intermediul membrilor clasei care conţine. Animal 1
Astfel, controlul asupra membrilor clasei
contineAlapteaza: Alapteaza
conţinute va fi total, putându-se controla care
din ei sa fie expuşi şi care nu. +Hraneste()
+Reproduce()
De exmplu, clasa Mamifer poate
conţine clasa Alaptează,care are metoda
publică Lapte(). Clasa Mamifer poate apela Figura 8
această metodă dacă este nevoie, de exmplu
prin intermediul metodei Hraneste() (fig. 8).
1.3.6 Colecţia
Un tablou unidimensional de obiecte de acleaşi tip
poate fi declarat ca mai jos: Animal
0..*
7
implementează de obicei metode de tipul Add() şi Remove() care adaugă sau şterg
elemente din colecţie. De asemenea, este în general utilizată o proprietate de tip Item,
care returnează obiectul de la un indice dat. Reprezentarea UML a unei astfel de colecţii
poate fi vazută în fig. 9. Prin marcajul 0..* se specifică faptul ca colecţia Animale va
conţine zero sau mai multe obiecte de tip Animal.
O descriere pe larg a POO, în sens clasic, puteţi găsi în Anexa 1.
1.3.7 Evenimente
Obiectele pot genera evenimente (events). Pentru aceasta, codul va trebui
completat cu un handler (event handler), care în fapt este o funcţie specială, ce va trebui
apelată pentru generarea unui eveniment specific. Acestă funcţie, trebuie de asemnea să
fie capabilă să asculte (listen) pentru a detecta producerea evenimentului specific. În
acest mod, se poate crea o aplicaţie orientată pe evenimente (event-driven). Aplicaţiile
Windows sunt în totalitate aplicaţii conduse de evenimente. Orice acşiune asupra
interfeţei, apăsarea unui buton, scrierea unei litere, mişcarea mouse-lui, va genera un
astfel de eveniment. Astfel, intrările-ieşirile din program devin asincrone cu execuţia lui.
1.4.1 Toolbox
Prin intermediul Toolbox-ului, programatorul are
la dispoziţie toate tipurile de controale necesare pentru
construirea unei aplicaţii. Toolbox-ul poate fi
particularizat pentru a satisface nevoile programatorului,
dar pe moment vom studia moful de utilizare al toolbox-
ului implicit.
8
• Anchor – prin această proprietate se poate specifica comportamenul controlului la
redimensionarea ferestrei ce-l conţine. Ancorarea lui de una din marginile ferestrei va
face ca poziţia acestuia faţă de marginea respectivă să nu se schimbe. Ancorarea lui
de toate marginile ferestrei va avea ca efect redimensionarea controlului odată cu
redimensionarea ferestrei.
• BackColor – stabileşte culoarea de fond a controlului.
• Bottom – stabileşte distanţa de la marginea de sus a ferestrei la marginea de jos a
controlului.
• Dock – ataşează controlul de una din marginile ferestrei.
• Enabled – activează controlul, adică permite acestuia să recepţioneze evenimente de
la utilizator. Dacă această proprietate este FALSE, controlul este inactiv.
• ForeColor – defineşte culoarea textului.
• Height – defineşte înălţimea controlului.
• Left – defineşte marginea stângă a controlului, relativ la marginea stângă a ferestrei.
• Name – identificatorul controlului. Poate fi utilizat în cod pentru a individualiza şi
accesa controlul.
• Parent – părintele controlului.
• Right - defineşte marginea dreaptă a controlului, relativ la marginea stângă a
ferestrei.
• TabIndex – numărul asociat controlului în ordinea de tab. Ordinea de tab stabileşte
ordinea in care controalele primesc input focusul (se selectează) la apăsarea tastei
TAB.
• TabStop – Stabileşte dacă controlul primeşte input focusul la apăsarea tastei TAB.
Dacă controlul nu are această proprietate, la apăsarea tastei TAB va fi sărit.
• Tag – este o proprietate care în general nu este utilizată de control. Poate fi doar un
şir de caractere, care este stocat în interiorul controlului.
• Top - stabileşte distanţa de la marginea de sus a ferestrei la marginea de sus a
controlului.
• Visible – dacă această proprietate este TRUE, controlul este vizibil. În caz contrar,
deşi există în formă, el nu poate fi vazut.
• Width - stabileşte lăţimea controlului.
• Click – este generat când se apasă click cu mouse-ul asupra unui control. Poate fi
uneori generat şi de apăsarea tastei Enter.
9
• DoubleClick – este generat cînd se apasă dublu click asupra unui control. Dacă
controlul este buton, acest eveniment nu va putea fi generat, pentru ca la prima
apăsare se generează automat Click.
• DragDrop – este generat când se finalizează (este eliberat butonul mouse-lui) o
operaţie drag-and-drop în care este implicat controlul.
• DragEnter – este generat atunci când obiectul implicat într-o operaţie drag-and-drop
ajunge în interiorul controlului.
• DragLeave - este generat atunci când obiectul implicat într-o operaţie drag-and-drop
părăseşte suprafaţa controlului.
• DragOver – este generat când un obiect implicat într-o operaţie drag-and-drop
ajunge deasupra controlului.
• KeyDown – este generat când o tastă devine apăsată în timp ce controlul deţine input
focusul. Se generează întotdeauna înainte de KeyPress şi KeyUp.
• KeyPress - este generat când o tastă devine apăsată în timp ce controlul deţine input
focusul. Se generează întotdeauna după KeyDown şi înainte de KeyUp. Spre deosebire
de KeyDown care furnizează codul de scanare al tastei apăsate, KeyPress furnizează
codul ascii al tastei.
• KeyUp - este generat când o tastă este eliberată în timp ce controlul deţine input
focusul. Se generează întotdeauna după KeyDown şi KeyPress.
• GotFocus – este generat când controlul primeşte input focusul.
• LostFocus – este generat când controlul pierde input focusul.
• MouseDown – este generat când prompterul mouse-lui este deasupra controlului şi
se apasă o tastă a mouse-lui.
• MouseMove – este generat continuu atâta timp cât prompterul mouse-lui traversează
controlul.
• MouseUp - ste generat când prompterul mouse-lui este deasupra controlului şi se
eliberează o tastă a mouse-lui.
• Paint – se generează la desenarea controlului.
• Validated – este generat când un control este în curs de a primi focusul. Se
generează după ce evenimentul Validating se termină şi indică faptul că validarea
controlului este completă.
• Validating - ste generat când un control este în curs de a primi focusul.
10
Urmează să-i asociem un handler de eveniment. Dacă apăsăm dublu click pe
buton, Form Designer va adăuga un handler asociat evenimentului Click şi ne va permite
să-i completăm codul. Observăm că s-a generat următorul cod:
Proprietatea Text
Figura 11
namespace unu
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
}
11
mouse-lui în suprafaţa butonului, respectiv părăsirea acesteia. Aceste evenimente sunt
MouseHover şi MouseLeave. Vom completa codul ca mai jos:
public Form1()
{
InitializeComponent();
button1.MouseHover += new EventHandler(button1_MouseHover);
button1.MouseLeave += new EventHandler(button1_MouseLeave);
}
}
private void button1_MouseLeave(object sender, EventArgs e)
{
Observaţii:
1. Observaţi că în timp ce scriem codul, Form Designer ne oferă informaţii asupra
codului şi îl poate completa singur (la apăsarea tastei TAB).
2. Se poate vedea că atributul BackColor l-am modificat în 2 moduri: fie prin intermediul
numelui button1, fie cu secvenţa ((Button) sender). Este corect oricum, pentru că
handlerul primeşte în argumentul sender numele obiectului care a generat evenimentul.
12
Compilaţi programul (Build, Build Solution) şi executaţi-l (Debug, Start Without
Debugging). Observaţi efectul trecerii prompterului mouse-lui pe deasupra butonului.
Haideţi să încercăm acum să creăm dinamic, prin program, un nou buton. Acesta
va fi creat la apăsarea butonului button1, deci codul va fi implementat în handlerul
asociat acestui eveniment.
…
private void button1_MouseLeave(object sender, EventArgs e)
{
((Button)sender).BackColor = culoare;
}
…
private Color culoare;
private bool apasat = false;
…
private void butonnou_Click(object sender, EventArgs e)
{
int i;
if (apasat == false)
{
((Button)sender).ForeColor = Color.Green;
apasat = true;
for (i = 0; i < 60; i++)
{
int j, k;
j = 1000;
while (j >= 0)
{
k = 10000;
13
do
k--;
while (k >= 0);
j--;
}
((Button)sender).Width--;
((Button)sender).Height--;
}
}
else
{
((Button)sender).ForeColor = Color.Red;
apasat = false;
for (i = 0; i < 60; i++)
{
((Button)sender).Width++;
((Button)sender).Height++;
}
}
}
14