Programare orientată pe obiecte

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare

În informatică , programarea orientată pe obiecte (în engleză programare orientată pe obiect, în acronim OOP) este o paradigmă de programare care permite definirea obiectelor software capabile să interacționeze între ele prin schimbul de mesaje. Deosebit de potrivit în contextele în care relațiile de interdependență pot fi definite între conceptele care urmează să fie modelate (izolare, utilizare, specializare), un domeniu care mai mult decât altele reușește să exploateze avantajele programării orientate pe obiecte este cel al interfețelor grafice .

Alte beneficii ale programării orientate pe obiecte includ:

  • oferă un suport natural modelării software a obiectelor din lumea reală sau a modelului abstract care trebuie reprodus;
  • permite gestionarea și întreținerea mai ușoară a proiectelor mari;
  • organizarea codului sub formă de clase favorizează modularitatea și reutilizarea codului .

Istorie

Conceptul de clasă poate fi considerat moștenitorul tipului de date abstracte , un concept care s-a dezvoltat inițial în cadrul paradigmei programării procedurale , care prevede definirea tipurilor de date cu care se poate interacționa doar printr-o interfață bine definită, ascunzând detalii a implementării către utilizator.
Construcțiile sintactice care permit definirea unei clase, în limbaje orientate obiect, pot fi văzute ca un suport structurat pentru crearea de date abstracte.

Primul limbaj de programare orientat pe obiecte a fost Simula ( 1967 ), urmat în anii 1970 de Smalltalk și diverse extensii ale Lisp . În anii 1980, au fost create extensii orientate pe obiecte ale limbajului C ( C ++ , Objective-C și altele) și alte limbaje ( Object Pascal ). În anii 1990 a devenit paradigma dominantă, prin care majoritatea limbajelor de programare erau fie orientate în mod nativ pe obiect, fie aveau o extensie în acest sens. Limbajele care acceptă doar paradigma de programare orientată obiect sunt Smalltalk și Eiffel . Mai des întâlnim o implementare neexclusivă a paradigmei de programare orientată obiect, ca în C ++ , Java , Delphi , Python , C # , Visual Basic .NET , Perl , PHP (începând cu versiunea 4).

Descriere

Programarea orientată pe obiecte implică gruparea într-o zonă limitată a codului sursă (numită clasă ), declararea structurilor de date și procedurile care operează pe acestea. Prin urmare, clasele constituie modele abstracte, care în timpul rulării sunt invocate pentru a crea sau crea obiecte software legate de clasa invocată. Acestea din urmă sunt echipate cu atribute (date) și metode (proceduri) așa cum sunt definite / declarate de clasele respective.

Partea programului care folosește un obiect se numește client .

Un limbaj de programare este orientat pe obiecte atunci când permite implementarea a trei mecanisme folosind sintaxa nativă a limbajului [1] :

Incapsularea constă în separarea așa-numitei interfețe a unei clase de implementarea sa corespunzătoare, astfel încât clienții unui obiect din acea clasă să poată utiliza prima, dar nu a doua.

Moștenirea vă permite, în esență, să definiți clase începând de la altele deja definite.

Polimorfismul vă permite să scrieți un client care poate utiliza obiecte din clase diferite, dar cu aceeași interfață comună; în timpul rulării, clientul respectiv va declanșa comportamente diferite fără a cunoaște a priori tipul specific al obiectului care i se transmite.

Clase

Pictogramă lupă mgx2.svg Același subiect în detaliu: Classroom (informatică) .

Clasele definesc tipurile de date și permit crearea de obiecte în funcție de caracteristicile definite în clasă. Datorită relațiilor de moștenire , este posibil să se creeze clase noi din cele existente, extinzându-le cu caracteristici suplimentare.

Clasa este formată din:

  • atribute (analog cu membrii unei înregistrări ), adică ale variabilelor și / sau constantelor care definesc caracteristicile sau proprietățile obiectelor care pot fi instanțiate prin invocarea clasei; valorile inițializate ale atributelor se obțin prin așa-numitul constructor ;
  • metode , adică proceduri care operează pe atribute.

O comparație (imprecisă) cu matematica este următoarea: se poate crede că o clasă definește un set într-un mod intensiv, adică indicând caracteristicile sale mai degrabă decât enumerând elementele sale și că obiectele sunt elementele acelui set. Cu toate acestea, în matematică, numărul de elemente este o caracteristică intrinsecă a setului în sine și este definit atunci când setul este definit, în timp ce în programare este posibilă instanțierea unei clase de un număr arbitrar de ori (teoretic, de la zero la infinit; în practică, de la zero până când memoria computerului este epuizată) și care depinde de execuția programului. Din acest motiv, este de preferat să considerăm clasa ca un model abstract instantaneu.

Cu alte cuvinte, o clasă este comparabilă cu proiectul unei infrastructuri care poate fi apoi pusă în funcțiune / operație sau realizată sau nu cu instanțierea obiectelor sale toate cu aceleași caracteristici sau cu atributele (cu valori diferite), pe care metodele sau funcțiile vor funcționa.

Obiecte

Pictogramă lupă mgx2.svg Același subiect în detaliu: Obiect (calcul) .

Un obiect este o instanță a unei clase. Este echipat cu toate atributele și metodele definite de clasă și acționează ca un furnizor de „mesaje” (metodele) pe care codul executabil al programului (proceduri sau alte obiecte) le poate activa la cerere.
Trimiterea unui mesaj către un obiect înseamnă, în jargon, invocarea unei metode pentru acel obiect. Metoda primește ca parametru (adesea implicit) obiectul pe care a fost invocată, care poate fi referit printr-un cuvânt cheie sau o sintaxă specifică, chiar dacă este trecut ca parametru implicit; de exemplu, în C ++ , Java și C # this cuvântul cheie this ( $this în PHP), în timp ce în Smalltalk , Objective-C , Python și Ruby self cuvântul cheie self .

Din punctul de vedere al computerului, fiecare obiect este identificat printr-o anumită zonă de memorie , în care sunt stocate atributele , iar valoarea acestuia din urmă determină starea internă a obiectului. Instanțierea unui obiect înseamnă alocarea memoriei și inițializarea acesteia în conformitate cu specificațiile definite de clasă. Multe limbi oferă suport pentru inițializarea automată a unui obiect, cu una sau mai multe metode speciale, numite constructori . În mod similar, sfârșitul vieții unui obiect poate fi tratat cu o metodă numită distructor .

Codul executabil al programului accesează această zonă de memorie întotdeauna și numai în modul definit de clasă.
Conform principiului cunoscut sub numele de ascunderea informațiilor , accesul la câmpurile unui obiect trebuie permis doar prin metode invocate pe același obiect. Principalul avantaj este că controlul complet asupra stării interne este atribuit unei zone restricționate a codului executabil al programului (clasa), deoarece codul extern nu este autorizat să îl modifice. În acest caz, este posibil să se impună constrângeri asupra valorilor posibile pe care atributul tuplu le poate sau nu să le asume și, de asemenea, asupra posibilelor tranziții între aceste stări. Prin urmare, un obiect poate fi văzut ca o mașină de stare finită .

Incapsularea

Pictogramă lupă mgx2.svg Același subiect în detaliu: Encapsulare (informatică) .

Incapsularea este proprietatea prin care datele care definesc starea internă a unui obiect și metodele care definesc logica acestuia sunt accesibile metodelor obiectului în sine, în timp ce nu sunt vizibile pentru clienți. Pentru a modifica starea internă a obiectului, este necesar să invocăm metodele sale publice și acesta este scopul principal al încapsulării. De fapt, dacă este gestionat corect, vă permite să vedeți obiectul ca o cutie neagră , adică o „cutie neagră” cu care interacțiunea are loc numai și numai prin metodele definite de interfață. Ideea este de a oferi utilizatorilor funcționalitate ascunzând detaliile legate de implementarea lor. Un exemplu ar putea fi un obiect „matematic” care dintre numeroasele operații pe care le oferă este unul care înmulțește două numere. De exemplu multiply (2,3) returnează 6. Acum algoritmul de multiplicare poate fi unul dintre mulți algoritmi care există, dar acest lucru pentru cei care apelează metoda multiply () nu are nicio diferență. Important este că rezultatul nu este greșit.

Moştenire

Pictogramă lupă mgx2.svg Același subiect în detaliu: Moștenirea (informatică) .

Mecanismul de moștenire este utilizat în faza de structurare / definire / planificare a software-ului sau în extensiile ulterioare și permite derivarea de noi clase pornind de la cele deja definite, creând o ierarhie de clase. O clasă derivată prin moștenire ( subclasă sau clasă copil), menține metodele și atributele claselor din care derivă ( clase de bază , superclase sau clase părinte); în plus, își poate defini propriile metode sau atribute și poate redefini codul unora dintre metodele moștenite printr-un mecanism numit suprascriere .

Când o clasă moștenește dintr-o singură super-clasă, aceasta este denumită moștenire unică; invers, vorbim de moștenire multiplă. Unele limbi (Java, Smalltalk printre altele) oferă suport unic pentru moștenirea unică. Altele (C ++, Python) asigură, de asemenea, moștenirea multiplă.

Unele limbaje tastate (Java, C #) asigură moștenirea interfețelor, precum și a claselor.

Moștenirea poate fi utilizată ca un mecanism pentru a realiza extensibilitatea și reutilizarea codului și este deosebit de avantajoasă atunci când este utilizată pentru a defini subtipuri , exploatând relațiile is-a existente în realitate, a căror structură de clasă este o modelare. În plus față de reutilizarea evidentă a codului superclasei, moștenirea permite definirea codului generic prin mecanismul polimorfismului.

Exemplu

Dacă există deja o clasă MezzoDiTrasporto în program care are drept proprietate datele privind poziția, viteza, destinația și sarcina utilă și este necesară o nouă clasă de Avion , este posibil să o creați direct din obiectul MezzoDiTrasporto declarând o clasă a Avionului tip care moștenește de la MezzoDiTrasporto și adăugând și atributul care identifică altitudinea de croazieră , cu avantajul că noua clasă va dobândi toți membrii definiți în MezzoDiTrasporto prin faptul că este chiar o subclasă.

Subtipare

Pictogramă lupă mgx2.svg Același subiect în detaliu: Subtipul (informatică) .

Deși conceptual există diferențe bine marcate, mecanismul de moștenire între clase ( subclasare ), prin mecanismul polimorfismului prin incluziune, permite limbajelor orientate obiect să modeleze moștenirea între tipuri ( subtipare ).

Conform principiului substituției Liskov , un tip S este un subtip de T atunci când este posibil să se înlocuiască toate instanțele lui T cu instanțele lui S , menținând programul funcțional intact.

Cu măsurile de precauție adecvate este posibil să se creeze o relație clasă-subclasă care să respecte și constrângerile relației tip-subtip. Din punct de vedere sintactic, acest lucru necesită ca toate metodele superclasei să fie prezente în subclasă și că semnăturile lor respective sunt compatibile. În consecință, o subclasă care dorește să definească un subtip poate redefini metodele superclasei, dar nu le poate șterge. Semnătura metodei poate fi modificată doar parțial, respectând constrângerile asupra variației parametrilor față de lanțul de moștenire.

Cu toate acestea, respectarea restricțiilor sintactice nu este suficientă, în sine, pentru a asigura conformitatea cu condiția Liskov: redefinirea metodelor sau reatribuirea atributelor [ neclar ] , de fapt, ar putea compromite compatibilitatea în timpul rulării.

În multe limbi, atunci când definiți o subclasă, puteți decide să ștergeți sau să modificați proprietățile de acces ale unei metode moștenite. În acest caz, operația de subclasare nu se potrivește cu operația de subtipare . Unele limbaje orientate obiect, în special Sather , împart în mod explicit subclasarea și subtiparea la nivel sintactic.

În limbile cu tastare statică și explicită , subtiparea este susținută prin polimorfism prin includerea subclaselor: aceeași variabilă se poate referi la un obiect de tipul pentru care a fost declarată sau tipuri derivate din acesta. Tipul obiectului identificat de variabilă, prin urmare, este definit la runtime ( legare dinamică ) și poate fi modificat în timpul executării programului.

Polimorfism

Pictogramă lupă mgx2.svg Același subiect în detaliu: Polimorfism (informatică) .

În programarea orientată obiect, denumirea de polimorfism prin incluziune indică faptul că același cod executabil poate fi utilizat cu instanțe de clase diferite, având o superclasă comună.

Legare dinamică sau polimorfism orizontal

Pictogramă lupă mgx2.svg Același subiect în detaliu: Obligatoriu .

Polimorfismul este deosebit de util atunci când versiunea metodei de executat este aleasă pe baza tipului de obiect conținut într-o variabilă în timpul rulării (mai degrabă decât în ​​timpul compilării ). Această caracteristică se numește legare dinamică (sau legare tardivă) și este susținută de cele mai populare limbaje de programare orientate pe obiecte.

Dacă o variabilă de tip A are două subtipuri (subclase) B și C , ambele suprascriind metoda m () , obiectul conținut în variabilă poate fi de tip A , B sau C și când metoda m este invocată pe variabila () execută versiunea adecvată pentru tipul de obiect conținut în variabilă la acel moment.

Pentru a reveni la exemplul anterior, se presupune că un avion trebuie să se confrunte cu proceduri de sosire și plecare mult mai complexe decât cele ale unui camion normal, așa cum este de fapt: atunci procedurile de sosire () și plecare () trebuie schimbate comparativ cu cele din clasa de bază MezzoDiTrasporto . Acestea sunt apoi redefinite în clasa Avion pentru a obține comportamentul necesar (polimorfism): în acest moment, din lista vehiculelor este posibil să luați orice mijloace și să apelați sosirea () sau plecarea () fără a vă face griji cu privire la ce este obiectul pe care îl manevrați: indiferent dacă este un vehicul normal sau un avion, acesta se va comporta întotdeauna răspunzând la același apel în modul corect.

Suportul pentru legarea dinamică nu este neapărat automat în toate limbajele de programare orientate pe obiecte. De exemplu, în Java, Smalltalk, Python, Ruby, legarea dinamică este implicit utilizată ca comportament implicit în clasele polimorfe, în timp ce în C ++ este activată prin inserarea cuvântului cheie virtual în semnătura metodei respective.

Suportul în timp de execuție al unui apel de metodă polimorfă necesită ca o variabilă polimorfă să fie asociată cu metadate implicite care conține tipul de date conținute în variabilă la un moment dat sau tabelul funcțiilor polimorfe.

Exemplu - Legare dinamică

Să presupunem că aveți următorul pseudo cod în Java unde Class2 este o subclasă a Class1 :

 metoda void ( intrare int ) {
    Clasa 1 c ;
    
    dacă ( intrare > 0 )
        c = clasa nouă ();
    altceva
        c = clasa nouă ();
    
    c . doSomethingImportant ();
}

Rețineți că doSomethingOfImportant este o metodă definită de Clasa 1, dar este posibil să fi fost redefinită în Clasa 2 (pentru că poate trebuie să fie tipărită diferit). Compilatorul nu poate ști la momentul compilării dacă există un obiect asociat clasei Class1 sau Class 2. Prin legarea dinamică, alegerea metodei de legătură efectivă va fi făcută în timpul execuției ( runtime ), când metoda este efectiv apelată.

Polimorfism vertical

Prin expresia polimorfism vertical ne referim la posibilitatea de a redefini sau reutiliza metode chiar și în clase derivate.

Probleme ale limbajelor OOP

Unele mecanisme incluse în gestionarea obiectelor provoacă o cheltuieli generale în ceea ce privește timpul și memoria, ceea ce în anumite situații poate duce la probleme de eficiență.

Unele limbi, cum ar fi Java și C ++, preferă o abordare hibridă decât OOP „pură”, cum ar fi limbajul Smalltalk , cu condiția ca unele tipuri de date primitive să nu fie tratate ca obiecte. Avantajele acestei abordări sunt evidente în special în prezența calculelor numerice; pe de altă parte, ori de câte ori este nevoie de un obiect în locul unui tip primitiv, este necesar să se recurgă la un înveliș special, iar acest lucru poate duce la scăderi de performanță.

Acest wrapper poate fi dat și automat de limbajul însuși, ca în cazul Java sau C # , printr-o conversie automată numită „autoboxing”. Vă permite să introduceți valori de tipuri primitive (de exemplu, numere întregi, caractere) în interiorul obiectelor, apoi să apelați părți de cod care ar dori obiecte fără a scrie în mod explicit instrucțiunea de creare a obiectului care cuprinde valoarea. Un astfel de box rezolvă problema scrierii manuale a unui wrapper, dar, desigur, performanța rămâne.

Alte critici ale POO privesc complexitatea structurală mai mare în ceea ce privește limbajele procedurale, în fața limitărilor introduse pentru a urma paradigma orientată pe obiecte. Există, de asemenea, unele deficiențe conceptuale (în special în ceea ce privește subtiparea), care adăugiri la diferite implementări în limbi pot provoca probleme programatorului. De exemplu, funcționarea în contexte polimorfe nu este garantată atunci când metodele sunt redefinite într-un lanț de moștenire. Mai mult, schimbarea definițiilor din clasele de bază poate duce la erori în cascadă în subclasele ( problema fragilă a clasei de bază ).

Notă

  1. ^ Aceste mecanisme pot fi, de asemenea, simulate în limbaje care nu sunt considerate orientate obiect, atâta timp cât sunt adoptate alte construcții și sunt respectate anumite convenții atunci când se scrie textul programului.

Bibliografie

Elemente conexe

linkuri externe

Controlul autorității Tezaur BNCF 57290 · LCCN (EN) sh87007503 · GND (DE) 4233947-9 · BNF (FR) cb12115131k (dată) · BNE (ES) XX537453 (dată) · NDL (EN, JA) 00.93798 milioane
Informatică Portal IT : accesați intrările Wikipedia care se ocupă cu IT