Documente Academic
Documente Profesional
Documente Cultură
(OOP)
Să presupunem că doriți să vă asamblați propriul computer, mergeți la un magazin de hardware
și luați o placă de bază, un procesor, câteva stick-uri de memorie RAM, un solid state drive (SSD), o
carcasă, o sursă de alimentare și le puneți împreună. Porniți alimentarea și computerul pornește.
Nu trebuie să vă faceți griji dacă procesorul este cu 1 sau 6 nuclee; placa de baza este cu 4 sau 6
straturi; RAM-ul este fabricat în Japonia sau Coreea și așa mai departe. Pur și simplu puneți
componentele hardware împreună și vă așteptați să funcționeze. Desigur, trebuie să vă asigurați
că aveți interfețele corecte, adică alegeți un SSD de tip M2 mai degrabă decât SSD de tip SATA,
dacă placa de bază acceptă doar M2; trebuie să selectați RAM cu viteza corectă și așa mai departe.
Cu toate acestea, nu este dificil să configurați o mașină din componente hardware.
Ce zici de software? Puteți „asambla” o aplicație software alegând o rutină aici, o rutină acolo și să
vă așteptați ca programul să ruleze? Raspunsul este evident nu. Spre deosebire de hardware, este
foarte dificil să „asamblezi” o aplicație din componente software. De la apariția computerului,
acum ~70 de ani, am scris tone și tone de programe și rutine. Totuși, pentru fiecare nouă
aplicație, trebuie să reinventăm roțile și să scriem programul de la zero.
Programele procedurale sunt alcătuite din funcții. Funcțiile sunt mai puțin reutilizabile. Este
foarte dificil să copiați o funcție dintr-un program și să o refolosiți într-un alt program,
deoarece este probabil ca funcția să facă referire la variabilele globale și la alte funcții. Cu
alte cuvinte, funcțiile nu sunt bine încapsulate ca o unitate reutilizabilă autonomă.
Limbajele procedurale nu sunt potrivite pentru abstractizare la nivel înalt pentru rezolvarea
problemelor din viața reală. De exemplu, programele C folosesc constructe precum if-else ,
for-loop , array , method , pointer , care sunt probleme reale de nivel scăzut și greu de
abstractizat, cum ar fi un sistem de management al relațiilor cu clienții (CRM) sau o bibliotecă
pe computer.
Unitatea de bază a OOP este o clasă, care încapsulează atât proprietățile statice, cât și
operațiile dinamice într-o „cutie” și specifică interfața publică pentru utilizarea acestora.
Deoarece clasele sunt bine încapsulate, este mai ușor să reutilizați aceste clase. Cu alte
cuvinte, OOP combină structurile de date și algoritmii unei entități software în interiorul
aceleiași entități.
Limbajele OOP permit un nivel mai ridicat de abstractizare pentru rezolvarea problemelor din
viața reală. Limbajul procedural tradițional (cum ar fi C și Pascal) vă obligă să gândiți în
termeni de structura computerului (de exemplu, biți și octeți de memorie, matrice, decizie,
buclă), mai degrabă decât să gândiți în termenii problemei pe care încercați să o rezolvați.
Limbajele OOP (cum ar fi Java, C++ și C#) vă permit să gândiți în spațiul problemei și să
utilizați obiecte pentru a reprezenta și a abstractiza entitățile din spațiul problemei pentru a
rezolva problema.
De exemplu, să presupunem că doriți să scrieți un program pentru organizarea unei biblioteci.
Este destul de dificil să modelezi programul în limbaje procedurale. Dar folosind limbaje OOP,
puteți modela cu ușurință programul în funcție de „lucrurile reale” care apar într-o locație reală.
Cel mai important, unele dintre aceste clase (cum ar fi Carte sau Autor) pot fi reutilizate într-o
altă aplicație, de exemplu, un magazin online de cărți, cu modificări reduse sau deloc.
Beneficiile OOP
Limbile procedurale se concentrează pe proceduri, cu funcția ca unitate de bază. Mai întâi trebuie
să vă dați seama de toate funcțiile și apoi să vă gândiți cum să reprezentați datele.
Ușurință în proiectarea software-ului: așa cum ați putea gândi în spațiul cu probleme, mai
degrabă decât în biți și octeți ai mașinii. Aveți de-a face cu concepte și abstracții de nivel înalt.
Ușurința în proiectare duce la o dezvoltare de software mai productivă
Ușurință în întreținerea software-ului: software-ul orientat pe obiecte este mai ușor de
înțeles, prin urmare mai ușor de testat, depanat și întreținut.
Software reutilizabil: nu trebuie să continuați să reinventați roata și să rescrieți aceleași
funcții pentru diferite situații. Cel mai rapid și mai sigur mod de a dezvolta o nouă aplicație
este reutilizarea codurilor existente - cod complet testat și dovedit.
OOP în Java
În Java, o clasă este o definiție a obiectelor de același fel. Cu alte cuvinte, o clasă este un model, un
șablon sau un prototip care definește și descrie atributele statice și comportamentele dinamice
comune tuturor obiectelor de același fel.
O instanță este o realizare a unui anumit element al unei clase. Cu alte cuvinte, o instanță este o
instanțiere a unei clase. Toate instanțele unei clase au proprietăți similare, așa cum este descris în
definiția clasei. De exemplu, puteți defini o clasă numită „Autor” și puteți crea trei instanțe ale
clasei „Autor” pentru „H.P Lovecraft”, „Haruki Murakami” și „Mary Shelley”.
Termenul „obiect” se referă de obicei la instanță. Dar este adesea folosit în mod liber și se poate
referi la o clasă sau o instanță.
O clasă poate fi vizualizată ca o cutie cu trei compartimente, după cum este ilustrat:
Nume
Atribute statice
Atribute dinamice
Nume (sau identitate): identifică clasa
Variabile (sau atribut, stare, câmp): conține atributele statice ale clasei.
Metode (sau comportamente, funcție, operație): conține comportamentele dinamice ale
clasei.
Cu alte cuvinte, o clasă încapsulează într-o cutie atributele statice (date) și comportamentele
dinamice (operațiile care operează pe date).
Author
Book
String fullName
String birthYear String name
int numberOfWrittenBooks int numberOfPages
ArrayList<String> genre boolean isBorrowed
ArrayList<Book> writtenBooks String returnByDate
De reținut
Modificatori de access
Un modificator de control al accesului poate fi utilizat pentru a controla vizibilitatea unei clase sau
a unei variabile membre sau a unei metode de membru în cadrul unei clase.
Private Da Nu Nu Nu
Default Da Da Nu Nu
Protected Da Da Da Nu
Public Da Da Da Da
De exemplu, să presupunem că avem o clasa numită Author , putem crea instanțe de Author
după cum urmează:
De exemplu, să presupunem că avem o clasă numită Author , cu mai multe variabile membre (
fullName, birthDate , etc.) și mai multe metode membre ( setNumberOfWrittenBooks() ,
getWrittenBooks() ). Am creat trei instanțe ale clasei Author , și anume, authorOne ,
authorTwo și authorThree . Pentru a invoca metoda getWrittenBooks() trebuie mai întâi să
identificați instanța de interes, să spunem secondAuthor , apoi să utilizați operatorul punct, sub
forma secondAuthor.getWrittenBooks() .
Constructor
Un constructor este o metodă specială care are același nume de metodă ca și numele clasei.
Adică, constructorul clasei Author se numește Author . În clasa Author de mai jos, vom defini
trei versiuni supraîncărcate ale constructorului Author(...) . Un constructor este folosit pentru
a construi și inițializa variabilele membre. Pentru a construi o nouă instanță a unei clase, trebuie
să utilizați operatorul special new urmat de un apel către unul dintre constructori. De exemplu:
// Primul constructor
public Author() { }
// Al doilea constructor
public Author(String fullName, String birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
// Al treilea constructor
public Author(String fullName, String birthYear, int numberOfWrittenBooks) {
this.fullName = fullName;
this.birthYear = birthYear;
this.numberOfWrittenBooks = numberOfWrittenBooks;
}
Acum vom folosi fiecare constructor pentru instanțierea a trei obiecte de tip Author
Numele metodei constructorului trebuie să fie același cu numele clasei. După convenția
numelui clasei, începe cu litere mari (în loc de litere mici pentru metodele obișnuite)
Constructorul nu are niciun tip de returnare în antetul metodei sale. Se întoarce implicit gol.
Nicio instrucțiune return nu este permisă în corpul constructorului
Constructorul poate fi invocat numai prin operatorul new . Poate fi folosit o singură dată
pentru a inițializa instanța construită. Odată ce o instanță este construită, nu mai puteți
apela constructorul
Constructorii nu sunt moșteniți (vor fi explicați mai târziu). Fiecare clasă își va defini propriii
constructori
Constructor implicit (default): Un constructor fără parametru este numit constructor implicit.
Inițializează variabilele membre la valorile lor implicite. De exemplu, Author() din exemplul de
mai sus inițializează raza și culoarea variabilelor membre la valorile implicite.
În codul de mai sus, există doi identificatori numiți fullName - o variabilă membru a clasei și
parametrul metodei. Acest lucru provoacă conflict de denumire. Pentru a evita conflictul de
denumire, puteți numi argumentul metodei name în loc de fullName . Cu toate acestea,
fullName este mai semnificativă în acest context. Java furnizează un cuvânt cheie numit this
pentru a rezolva acest conflict de denumire. this.fullName se referă la variabila membru; în
timp ce fullName se rezolvă la argumentul metodei.
Overloading
Supraîncărcarea metodei înseamnă că același nume de metodă poate avea implementări
(versiuni) diferite. Totuși, diferitele implementări trebuie să fie distinse prin lista lor de parametri
(fie numărul de parametri, fie tipul de parametri, fie ordinea acestora).
// Al doilea constructor
public Author(String fullName, String birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
// Al treilea constructor
public Author(String fullName, String birthYear, int numberOfWrittenBooks) {
this.fullName = fullName;
this.birthYear = birthYear;
this.numberOfWrittenBooks = numberOfWrittenBooks;
}
}
Encapsulation
O clasă încapsulează numele, atributele statice și comportamentele dinamice într-o „cutie cu 3
compartimente”. Odată ce o clasă este definită, puteți sigila „cutia” și puneți „cutia” pe raft pentru
ca alții să o utilizeze și să o refolosească. Oricine poate ridica „cutia” și o poate folosi în aplicația
sa. Acest lucru nu se poate face în limbajele procedurale, cum ar fi C, deoarece atributele (sau
variabilele) statice sunt împrăștiate pe întregul program și fișierele antet. Nu puteți „decupa” o
porțiune din programul C, conectați-vă la un alt program și așteptați ca programul să ruleze fără
modificări ample.
Variabilele membre ale unei clase sunt de obicei ascunse de cuvântul exterior (adică, celelalte
clase), cu modificatorul de control al accesului privat. Accesul la variabilele membrilor este
asigurat prin metode de evaluator public, e.g getNumberOfWrittenBooks() sau
getWrittenBooks()
Aceasta urmează principiul ascunderii informațiilor. Adică, obiectele comunică între ele folosind
interfețe bine definite (metode publice). Obiectele nu au voie să cunoască detaliile de
implementare ale altora. Detaliile implementării sunt ascunse sau încapsulate în clasă.
Ascunderea informațiilor facilitează reutilizarea clasei.
Regula generală: nu faceți publice nicio variabilă, decât dacă aveți un motiv întemeiat.
Author
String name
String email
char email
Trei variabile private: name (String), email (String) și gender („m” sau „f”)
Un constructor pentru a inițializa numele, e-mailul și sexul cu valorile date.
(Nu există un constructor implicit, deoarece nu există o valoare implicită pentru nume, e-mail
și sex.)
Getters/setters publici: getName() , getEmail() , setEmail() și getGender() .
(Nu există setari pentru nume și sex, deoarece aceste proprietăți nu sunt concepute pentru a
fi modificate.)
@Override
public String toString() {
return name + ", " + email + ", " + gender;
}
}
Să proiectăm o clasă de carte. Să presupunem că o carte este scrisă de un (și exact unul) autor.
Book
String name
Author author
double price
int quantity
Clasa Book (așa cum se arată în diagrama de clasă) conține următorii membri:
Patru variabile membre private: name ( String ), author (o instanță a clasei Author pe care
tocmai am creat-o, presupunând că fiecare carte are exact un autor), price ( double ) și
quantity ( int ).
Getterii și setarii publici: getName() , getAuthor() , getPrice() , setPrice() , getQty() ,
setQty() .
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author=" + author +
", price=" + price +
", qty=" + qty +
'}';
}
}
//Setters si Getters
firstBook.setPrice(8.88);
firstBook.setQty(88);
System.out.println("Numele este: " + firstBook.getName());
System.out.println("Pretul este: " + firstBook.getPrice());
System.out.println("Cantitatea este: " + firstBook.getQty());
System.out.println("Autorul este: " + firstBook.getAuthor()); //
Folosim toString() al clasei Author
//author is: Haruki Murakami, haruki.murakami@gmail.com, m
System.out.println("Numele autorului este: " +
firstBook.getAuthor().getName());
//author's name is: Haruki Murakami
System.out.println("Emailul autorului este: " +
firstBook.getAuthor().getEmail());
//author's email is: haruki.murakami@gmail.com
System.out.println("Genul autorului este: " +
firstBook.getAuthor().getGender());
//author's gender is: m
În OOP, organizăm adesea clase în ierarhie pentru a evita dublarea și pentru a reduce
redundanța. Clasele din ierarhia inferioară moștenesc toate variabilele (atribute statice) și
metodele (comportamente dinamice) din ierarhiile superioare. O clasă din ierarhia inferioară se
numește subclasă (sau derivată, copil, clasă extinsă). O clasă din ierarhia superioară se numește
superclasă (sau clasă de bază, părinte). Prin extragerea tuturor variabilelor și metodelor comune
în superclase și lăsând variabilele și metodele specializate în subclase, redundanța poate fi mult
redusă sau eliminată, deoarece aceste variabile și metode comune nu trebuie repetate în toate
subclasele.
O subclasă moștenește toate variabilele și metodele din superclasele sale, inclusiv părintele
imediat, precum și toți strămoșii. Este important de reținut că o subclasă nu este un „subset” al
unei superclase. În schimb, subclasa este un „superset” al unei superclase. Se datorează faptului
că o subclasă moștenește toate variabilele și metodele superclasei; în plus, extinde superclasa
oferind mai multe variabile și metode.
Exemplu
Person
String name
String birthDate
char gender
Person: Person()
Person: Person(name, birthDate)
Person: Person(name, birthDate, gender)
Person: getName()
Person: getBirthdate()
Person: getGender()
Person: setGender()
Teacher
String specialty
Teacher: Teacher()
Teacher: Teacher(name, birthDate)
Teacher: Teacher(name, birthDate, gender)
Teacher: Teacher(name, birthDate, gender, specialty)
Teacher: getSpecialty()
Teacher: setSpecialty()
În acest exemplu, derivăm o subclasă numită Teacher din superclasa Person . Este important de
reținut că reutilizam clasa Person . Reutilizarea este una dintre cele mai importante proprietăți
ale POO. (De ce să reinventezi roata?) Clasa Teacher moștenește toate variabilele membre
( name , birthDate și gender ) și metodele ( getName() , getBirthDate() , etc.) din superclasa
Person . Definește în continuare o variabilă numită speciality , două metode publice -
getSpecialty() și setSpecialty() și proprii săi constructori, după cum se arată:
package com.company;
// Constructori
public Person() {
this.name = "";
this.birthDate = "";
this.gender = '\0';
}
// Getter si Setter
public String getName() {
return name;
}
@Override
public String toString() {
return "Name: " + name +
"\nBirthdate: " + birthDate +
"\nGender: " + gender;
}
}
package com.company;
// Constructori
public Teacher() {
super();
specialty = "";
}
// Getter si Setter
public String getSpecialty() {
return specialty;
}
@Override
public String toString() {
return "Name: " + super.getName() +
"\nBirthdate: " + super.getBirthDate() +
"\nGender: " + super.getBirthDate() +
"\nSpecialty: " + specialty;
}
}
Regula generală: Folosiți compoziția dacă este posibil, înainte de a lua în considerare
moștenirea. Folosiți moștenirea numai dacă există o relație ierarhică clară între clase.
Polymorphism
Cuvântul „polimorfism” înseamnă „multe forme”. Provine din cuvântul grecesc „poli” (mulți) și
„morphos” (formă). De exemplu, în chimie, carbonul prezintă polimorfism deoarece poate fi găsit
sub mai multe forme: grafit și diamant. Dar, fiecare formular are propriile proprietăți distincte.
Polimorfismul este foarte puternic în OOP pentru a separa interfața și implementarea, astfel încât
să permită programatorului să programeze la interfață în proiectarea unui sistem complex.
Shape
String colour
Shape: getArea()
Shape: toString()
Rectangle Triangle
int length int base
int width int height
Rețineți că avem o problemă la scrierea metodei getArea() în clasa Shape , deoarece aria nu
poate fi calculată decât dacă forma reală este cunoscută. Vom printa un mesaj de eroare pentru
moment. În secțiunea ulterioară, vă voi arăta cum să rezolvați această problemă.
Frumusețea acestui cod este că toate referințele sunt din superclasă (adică, programarea la nivel
de interfață). Puteți instanția o altă instanță de subclasă, iar codul încă funcționează. Vă puteți
extinde cu ușurință programul adăugând mai multe subclase, cum ar fi Circle , Square etc., cu
ușurință.