Clase in detaliu
Ne vom focusa acum pe utilizarea claselor. Programarea orientata pe obiecte organizeaza codul si datele in obiecte. Dupa cum am vazut, clasele pot contine campuri (fields), proprietati (properties), contructori (constructors) si metode (methods) cu diferiti modificatori de acces.
- Campuri - sunt intrarile din structura obiectului asa cum se gasesc in memoria programului. Se scriu cu Pascal-Case in afara de situatia in care sunt private, atunci sunt scrise cu "_" in fata si restul Camel-Case.
- Metode - sunt functiile asociate clasei. Se scriu cu Pascal-Case.
- Contructori - sunt functii (in general, depinzand de limbaj pot avea alte nuante pentru interpretare) speciale care sunt apelate la crearea obiectului dupa ce zona de memorie a fost alocata prin cuvantul cheie new pentru a instantia obiectul. Se scriu cu Pascal-Case.
- Proprietati - Sunt metode speciale fara parametri expliciti care sunt accesate ca compurile dar de fapt sunt apelate intern ca functii. Proprietatile sunt echivalentul la getter si setter din Java sau alte limbaje dar le unesc sub o forma unica. Proprietatile au avantajul ca ascund computatii care se pot face la setare sau citire de variabile si pot avea sau nu compuri care sa le sprijine. Se scriu cu Pascal-Case.
Mai jos este un exemplu de clasa cu diferite campuri, proprietati si metode cu modificatori de acces si cuvinte cheie.
/*
* "internal" pentru clase si campuri face ca aceasta clasa sa fie vizibila doar in interiorul assembly-ului curent.
*/
internal class TestClass {
// "public" face sa fie vizibil oriunde poate fi vizibila clasa iar const pentru primitive este echivalentul la "static readonly".
public const int Constant = 42;
/* "readonly" face sa poata fi un camp doar initializat de catre un constructor si sa nu se mai poata fi schimbat.
* "private" face ca acest camp sa poata fi vazut doar de catre clasa care il contine.
*/
private readonly string _privateField;
// Se pot crea proprietati care sunt functii.metode care pot fi folosite ca membri dar sunt mereu calculati la fiecare citire.
public string DoubledProperty => _privateField + _privateField;
// Se pot crea proprietati care sa aiba getter si setter implicit pentru a lua si a seta valoare din spate fara un camp explicit declarat.
public int SecondProperty { get; set; }
private string _backedField;
/* Se pot face si proprietati care seteaza campuri private si implicand o computatie pe un camp in spate (backed field).
* Cuvantul cheie "value" la setter reprezinta valoarea data la scriere.
*/
public string ThirdProperty { get => _backedField; set => _backedField = value + value; }
public TestClass(string privateField)
{
_privateField = privateField;
}
/* Ca metoda non-statica exista referinta la propriile campuri, metode si proprietati
* si poate fi folosit referinta la propria instanta prin "this".
*/
public TestClass ConcatStringAndReturn(string other)
{
return new TestClass(this._privateField + other);
}
/* Pentru orice metoda putem acea un overload, o metoda cu acelasi nume
* dar lista de parametri cu tipuri diferite
*/
public TestClass ConcatStringAndReturn(TestClass other)
{
return new TestClass(this._privateField + other._privateField);
}
/* In metoda statica marcata prin cuvantul cheie "static" se pot referentia campuri private din clasa
* dar nu se poate refentia cuvantul cheie "this" pentru ca o metoda statica nu este legata de o instanta de obiect
*/
public static TestClass ConcatClasses(TestClass a, TestClass b)
{
return new TestClass(a._privateField + b._privateField);
}
}
Modificatori de acces
Exista diferiti modificatori de acces pentru clase, metode si campuri. Modificatorii de acces au ca rol sa restrictioneze accesul din diferite parti ale codului. Practic, programatorul se asigura ca datele dintr-o clasa sa nu poata fi accesate in alta parte a codului pentru ca altfel se pot produce erori. Principalii modificatori de acces sunt:
- public – orice cu acest modificator de acces este vizibil in tot codul sursa.
- protected – clasa sau membrul din clasa poate fi accesat doar de catre clase care o mostenesc (vedeti mostenire).
- private – clasa sau membri sai pot fi vazut doar de catre aceasta.
- internal – numit si default, daca nu este pus explicit un modificator de acces se pune acesta. Face ca clasa sau membri sai sa fie vizibili doar in propriul assembly, adica in propriul executabil sau in biblioteca care rezulta in urma compilarii.
O sumarizare a moficatorilor de acces este urmatoarea:
Locatia accesului | public | protected internal | protected | internal | private protected | private |
---|---|---|---|---|---|---|
In interiorul clasei | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Clasa derivata (acelasi assembly) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
Clasa non-derivata (acelasi assembly) | ✔️ | ✔️ | ❌ | ✔️ | ❌ | ❌ |
Clasa derivata (alt assembly) | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ |
Clasa non-derivata (alt assembly) | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
Modificatorii de acces trebuie folositi pentru a preintampina folosirea gresita a obiectelor. Daca prin accesul unei clase sau a unui membru poate exista un scenariu care sa produca o eroare acest scenariu poate fi eliminat prin atasarea unui modificator de acces.
Static vs. non-static
Cuvantul cheie static are 2 utilizari. Aplicat pe clase face ca acea clasa sa nu poata fi instantiata decat la initializarea programului de catre framework si forteaza ca campurile si metodele sa fie statice in clasa. Aplicat pe campuri sau metode face ca acestea sa fie folosite la fel de catre toate instantele de obiecte chiar si in afara clasei. Nu se mai poate referentia aici o intanta de obiect prin cuvantul cheie this.
Cand vorbim de metode non-statice si statice ne referim daca metoda are acces la o instanta de obiect prin cuvantul cheie "this" sau nu. De obicei, se zice ca diferenta intre cele doua este ca cele doua metode au un context diferit, unul non-static care difera prin instanta de obiect si unul static care e mereu acelasi. Contrar acestei conceptii raspandite adevarul este ca nu exista diferenta intre cele doua. Metodele sunt functii care stau in aceiasi zona de cod, diferenta este ca la metodele non-statice compilatorul pune, fara ca programatorul sa-si dea seama, primul parametru al functiei non-statice de tipul clasei cu valoarea instanta obiectului prin care se apeleaza metoda, primul parametru fiind acel "this".
Cand trebuie sa avem campuri si metode statice:
- nu se face referire la propria instanta
- cand avem nevoie de constante care sa fie vizibile in tot codul
- cand avem nevoie de functii clasice
Cand trebuie sa folosim campuri si metode non-statice:
- cand trebuie sa tinem variabile intr-un obiect pentru a urmarii o stare
- cand vrem sa folosim datele din acelasi obiect pentru mai multe computatii
Imutabilitate
Trebuie retinut aici ca obiectele pot fi imutabile (immutable) insemnand ca starea lor interna nu se schimba dupa creare. Pentru a avea obiectul modificat se creaza mereu o alta instanta cu modificarea. Un exemplu de obiect imutabil este tipul string, daca concatenam un string cu altul se creaza o instanta noua, cea anterioara ramane la fel. Imutabilitatea desi poate costa mai multa memorie, este un lucru dorit in multe scenarii, de exemplu daca codul este mare proportie functional se preteaza folosirea obiectelor imutabile deoarece efectele laterale pot afecta in mod negativ fluxul programului. La fel, si intr-un context de multi-threading daca obiectele sunt doar citite nu mai exista nevoia de metode de sincronizare intre fire de executie ce are un efect pozitiv asupra performantelor programului deoarece aceste mecanisme sunt mai costisitoare si simplifica fluxul programului prin evitarea lor.
Overloading
Un termen important in OOP este ca metodele pot fi supraincarcate (overloaded), insemnand ca pot exista mai multe metode, statice sau non-statice, cu acelasi nume dar signaturi diferite pentru aceiasi clasa. O anumita metoda cu un anumit set de parametri se numeste overload-ul acelei functii, acest lucru se aplica si la constructori.
Exercitii:
- Creati o lista simplu inlantuita folosind clase cu valori de tip int si de tip string avand operatii de adaugare, stergere si acces prin index.
- Creati o lista dublu inlantuita folosind clase cu valori de tip int si de tip string avand operatii de adaugare, stergere si acces prin index.
- Discutati ce ar putea sa imbunatateasca solutiile ca sa nu mai aveti cod duplicat.