Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • știri
  • Caracteristicile programării orientate pe obiecte. Principii de programare orientată pe obiecte

Caracteristicile programării orientate pe obiecte. Principii de programare orientată pe obiecte

Paradigma de programare

Programare orientată pe obiecte (OOP)- o metodologie de programare bazată pe reprezentarea unui program ca o colecție de obiecte, fiecare dintre acestea fiind o instanță a unei anumite clase, iar clasele formează o ierarhie de moștenire.

Trebuie acordată atenție următoarelor părți importante ale acestei definiții: 1) Programarea orientată pe obiecte folosește mai degrabă obiecte decât algoritmi ca blocuri logice principale; 2) fiecare obiect este o instanță a unei anumite clase; 3) clasele formează ierarhii. Un program este considerat orientat pe obiecte numai dacă toate aceste trei cerințe sunt îndeplinite. În special, programarea care nu folosește moștenirea se numește programare folosind tipuri de date abstracte, mai degrabă decât orientată pe obiecte.

YouTube colegial

    1 / 5

    ✪ Programare orientată pe obiecte în 2019

    ✪ Proiectare orientată pe obiecte Partea 1 - Cum sunt proiectate clasele

    ✪ Principii de bază ale programării orientate pe obiecte. Ce este OOP și de ce este necesar?

    ✪ Fundamentele OOP în C++

    ✪ Programare orientată pe obiecte. Clase și obiecte. Lecția 3

    Subtitrări

Noțiuni de bază

Abstracția datelor Abstracția înseamnă evidențierea informațiilor semnificative și excluderea din considerare a informațiilor nesemnificative. OOP ia în considerare doar abstracția datelor (denumită adesea pur și simplu „abstracție”), implicând un set de caracteristici semnificative ale unui obiect, disponibile pentru restul programului. Încapsulare Încapsularea este o proprietate de sistem care vă permite să combinați datele și metodele care funcționează cu acestea într-o clasă. Unele limbi (de exemplu, C++, Java sau Ruby) echivalează încapsularea cu ascunderea, dar altele (Smalltalk, Eiffel, OCaml) fac diferența între cele două. Moștenire Moștenirea este o proprietate de sistem care vă permite să descrieți o nouă clasă bazată pe una existentă cu funcționalitate împrumutată parțial sau complet. Clasa de la care moșteniți se numește bază, părinte sau superclasă. Noua clasă este o clasă descendentă, moștenitoare, copil sau derivată. Polimorfismul subtipului Polimorfismul subtipului (în POO numit simplu „polimorfism”) este o proprietate de sistem care vă permite să utilizați obiecte cu aceeași interfață fără informații despre tipul și structura internă a obiectului. Un alt tip de polimorfism - parametric - se numește programare generică în OOP. Clasa A este un tip de date universal, complex, format dintr-un set tematic unificat de „câmpuri” (variabile de tipuri mai elementare) și „metode” (funcții de lucru cu aceste câmpuri), adică este un model de entitate informațională cu interfețe interne și externe pentru operarea conținutului acestuia (valori de câmp). În special, în clase, sunt utilizate pe scară largă blocuri speciale din una sau mai multe două metode pereche, care sunt responsabile pentru operațiuni elementare cu un câmp specific (interfața de atribuire și citire a unei valori), care simulează accesul direct la câmp. Aceste blocuri se numesc „proprietăți” și sunt aproape identice ca nume specific cu câmpul lor (de exemplu, un nume de câmp poate începe cu o literă mică și un nume de proprietate cu o literă mare). O altă manifestare a naturii de interfață a unei clase este aceea că atunci când se copiază variabila corespunzătoare prin atribuire, este copiată doar interfața, dar nu și datele în sine, adică clasa este un tip de date de referință. O variabilă obiect de un tip specificat de o clasă se numește o instanță a acelei clase. Mai mult, în unele sisteme de rulare, o clasă poate fi reprezentată și de un obiect în timpul execuției programului prin identificarea dinamică a tipului de date. De obicei, clasele sunt concepute astfel încât să se asigure că integritatea datelor obiectului este în concordanță cu natura obiectului și problema care este rezolvată, precum și o interfață convenabilă și simplă. La rândul său, integritatea domeniului obiectelor și a interfețelor acestora, precum și comoditatea designului lor, este asigurată prin moștenire. Obiect O entitate din spațiul de adrese al sistemului de calcul care apare atunci când este creată o instanță a unei clase (de exemplu, după rularea rezultatelor compilației și conectarea codului sursă pentru execuție).

Clasificarea subspeciilor OOP

Luca Cardelliși Martin Abadi au construit o justificare teoretică pentru OOP și o clasificare bazată pe acest raționament. Ei observă că conceptele și categoriile pe care le-au identificat nu se găsesc împreună în toate limbile OO; majoritatea limbilor acceptă doar subseturi ale teoriei și, uneori, anumite abateri de la aceasta.

Cele mai vizibile diferențe în manifestarea indicatorilor de calitate între limbi de diferite tipuri:

  • În limbajele mainstream, principiile declarate vizează creșterea factorului de reutilizare a codului, care este inițial scăzut pentru programarea imperativă. În tipărirea polimorfică, utilizarea conceptelor OOP, dimpotrivă, înseamnă o scădere evidentă a acesteia datorită trecerii de la polimorfismul parametric la polimorfismul ad-hoc. Limbile tipizate dinamic (Smalltalk, Python, Ruby) folosesc aceste principii pentru a organiza logic un program, iar impactul lor asupra ratei de reutilizare este greu de prezis - depinde foarte mult de disciplina programatorului. De exemplu, în CLOS, multimetodele sunt simultan funcții de primă clasă, ceea ce le permite să fie considerate simultan și ca aferent cuantificat, și ca generalizat (cu adevărat polimorf).
  • Se folosesc limbi tradiționale OO tastare nominativă, adică admisibilitatea coutilizarii obiectelor din clase diferite numai sub condiția unei indicații explicite a relației dintre clase. Limbile tipizate polimorf se caracterizează prin tipărirea structurală, adică coordonarea claselor între ele este același mecanism ca și coordonarea numărului 5 cu tipul int. Limbile tastate dinamic ocupă, de asemenea, o poziție intermediară aici.

Rațiune generalizată dispecerare dinamica(inclusiv plural) construit de Giuseppe Castagna la mijlocul anilor 1990.

Poveste

OOP a apărut ca urmare a dezvoltării ideologiei programării procedurale, unde datele și subprogramele (proceduri, funcții) ale prelucrării lor nu sunt legate formal. Pentru dezvoltarea ulterioară a programării orientate pe obiecte, conceptele unui eveniment (așa-numita programare orientată pe evenimente) și o componentă (programarea componentelor, COP) sunt adesea de mare importanță.

Interacțiunea obiectelor are loc prin. Rezultatul dezvoltării ulterioare a OOP, aparent, va fi programarea orientată către agenți, unde agenţi- bucăți independente de cod la nivel de rulare. Interacțiunea agenților are loc prin schimbare miercuri in care sunt situate.

Construcțiile de limbaj care nu sunt structural direct legate de obiecte, dar care le însoțesc pentru funcționarea lor sigură (excepții, verificări) și eficientă, sunt încapsulate din ele în aspecte (în programarea orientată pe aspecte). Programarea orientată pe subiect extinde conceptul de obiect, oferind o interacțiune mai unificată și mai independentă între obiecte. Poate fi o etapă de tranziție între OOP și programarea bazată pe agenți în ceea ce privește interacțiunea lor independentă.

Primul limbaj de programare în care au fost propuse conceptele de bază, care s-au dezvoltat ulterior într-o paradigmă, a fost Simula, dar termenul de „orientare la obiect” nu a fost folosit în contextul utilizării acestui limbaj. La momentul apariției sale în 1967, în ea erau propuse idei revoluționare: obiecte, clase, metode virtuale etc., dar toate acestea nu au fost percepute de contemporani ca pe ceva grandios. De fapt, Simula a fost un Algol cu ​​clase, ceea ce făcea ușor de exprimat multe concepte complexe în programarea procedurală. Conceptul de clasă în Simul poate fi pe deplin definit prin alcătuirea constructelor Algol (adică o clasă în Simul este ceva complex, descris prin intermediul primitivelor).

Alan Kaye și Dan Ingalls i-au prezentat lui Smalltalk lui Alan Kaye și Dan Ingalls, analizând programarea dintr-o „nouă perspectivă” (alta decât procedurală). Aici conceptul de clasă a devenit ideea de bază pentru toate celelalte construcții ale limbajului (adică o clasă în Smalltalk este o primitivă prin care sunt descrise construcții mai complexe). El a devenit primul limbaj de programare orientat pe obiecte răspândit.

În prezent, numărul de limbaje de programare aplicate (lista de limbaje) care implementează paradigma orientată pe obiect este cel mai mare în raport cu alte paradigme. Cele mai comune limbaje din industrie (C ++, Delphi, C #, Java etc.) întruchipează modelul obiect Simula. Exemple de limbaje bazate pe modelul Smalltak sunt Objective-C, Python, Ruby.

Definiția POO și concepte de bază

În centrul OOP se află conceptul obiect. Un obiect este o entitate căreia îi pot fi trimise mesaje și care poate răspunde la acestea folosind datele sale. Un obiect este o instanță a unei clase. Datele obiectului sunt ascunse de restul programului. Ascunderea datelor se numește încapsulare.

Încapsularea este suficientă pentru ca un limbaj de programare să fie orientat pe obiecte, dar nu înseamnă că este orientat pe obiecte - necesită moștenire.

Dar chiar și prezența încapsulării și a moștenirii nu face ca limbajul de programare să obiecteze pe deplin din punctul de vedere al OOP. Principalele avantaje ale OOP apar doar atunci când limbajul de programare implementează polimorfismul subtipului - capacitatea de a manipula uniform obiecte cu implementări diferite, cu condiția să existe o interfață comună.

Dificultăți de determinare

OOP are o istorie de peste patruzeci de ani, dar, în ciuda acestui fapt, nu există încă o definiție clară, general acceptată, a acestei tehnologii. Principiile de bază stabilite în primele limbaje și sisteme obiect au suferit modificări (sau distorsiuni) și completări semnificative cu numeroase implementări ale perioadei ulterioare. În plus, încă de la mijlocul anilor 1980, termenul „orientat pe obiect” a devenit la modă, drept urmare i s-a întâmplat același lucru ca puțin mai devreme cu termenul „structurat” (care a devenit la modă după răspândirea structurilor structurate). tehnologia de programare) - a devenit artificial „atașat” oricăror noi dezvoltări pentru a le face atractive. Björn Stroustrup a scris în 1988 că rațiunea pentru „orientarea la obiect” a ceva, în cele mai multe cazuri, se rezumă la un silogism fals: „X este bun. Orientarea obiectelor este bună. Prin urmare, X este orientat pe obiecte.”

Roger King a susținut că pisica lui era orientată pe obiecte. Printre celelalte virtuți ale sale, pisica demonstrează un comportament caracteristic, reacționează la mesaje, este înzestrată cu reacții moștenite și își controlează propria stare internă, complet independentă.

Cu toate acestea, generalitatea mecanismului de mesagerie are o altă latură - mesageria „cu drepturi depline” necesită o suprasarcină suplimentară, ceea ce nu este întotdeauna acceptabil. Prin urmare, multe limbaje moderne de programare orientate pe obiecte folosesc conceptul „Trimiterea unui mesaj ca apel de metodă”- obiectele au metode accesibile din exterior, ale căror apeluri asigură interacțiunea obiectelor. Această abordare este implementată într-un număr mare de limbaje de programare, inclusiv C++, Object Pascal, Java, Oberon-2. Cu toate acestea, acest lucru duce la faptul că mesajele nu mai sunt obiecte independente și, ca urmare, nu au atribute, ceea ce restrânge posibilitățile de programare. Unele limbi folosesc reprezentarea hibridă, demonstrând beneficiile ambelor abordări în același timp - de exemplu, CLOS, Python.

Concept metode virtuale, susținută de acestea și de alte limbaje moderne, a apărut ca un mijloc de a asigura execuția metodelor necesare la utilizarea variabilelor polimorfe, adică, de fapt, ca o încercare de extindere a posibilităților de apelare a metodelor pentru a implementa o parte din funcționalitatea oferită de mecanismul de procesare a mesajelor.

Caracteristici de implementare

După cum am menționat mai sus, în limbajele moderne de programare orientate pe obiecte, fiecare obiect este o valoare asociată cu o anumită clasă. O clasă este un tip de date compus declarat de programator, care include:

Câmpuri de date Parametrii obiectului (desigur, nu toți, dar numai necesari în program) care specifică starea acestuia (proprietățile obiectului domeniului). Câmpurile de date obiect sunt uneori denumite proprietăți ale obiectului, ceea ce poate fi confuz. Din punct de vedere fizic, câmpurile sunt valori (variabile, constante) declarate ca aparținând clasei. Metode Proceduri și funcții asociate unei clase. Ele definesc acțiuni care pot fi efectuate asupra unui obiect de acest tip și pe care obiectul însuși le poate efectua.

Clasele pot moșteni unele de la altele. Clasa descendentă primește toate câmpurile și metodele clasei părinte, dar le poate completa cu propriile sale sau le poate înlocui pe cele existente. Majoritatea limbajelor de programare acceptă o singură moștenire (o clasă poate avea o singură clasă părinte), doar în unele limbaje de programare este permisă moștenirea multiplă - generarea unei clase din două sau mai multe clase părinte. Moștenirea multiplă creează o serie de probleme, atât logice, cât și pur de implementare, prin urmare suportul său deplin nu este larg răspândit. În schimb, în ​​anii 1990, conceptul de interfață a apărut și a început să fie introdus activ în limbajele orientate pe obiecte. O interfață este o clasă de implementare fără margini, care include doar anteturi de metodă. Dacă o clasă moștenește (sau, după cum se spune, implementează) o interfață, trebuie să implementeze toate metodele incluse în ea. Utilizarea interfețelor oferă o alternativă relativ ieftină la moștenirea multiplă.

Interacțiunea obiectelor în majoritatea absolută a cazurilor este asigurată prin apelarea metodelor reciproce.

Încapsularea se face prin următoarele mijloace:

Controlul accesului Întrucât metodele unei clase pot fi atât pur interne, oferind logica funcționării obiectului, cât și externe, cu ajutorul cărora obiectele interacționează, este necesar să se asigure secretul primelor atunci când acestea din urmă sunt accesibile din in afara. Pentru aceasta, în limbaje sunt introduse construcții sintactice speciale care stabilesc în mod explicit domeniul de aplicare al fiecărui membru al clasei. În mod tradițional, aceștia sunt modificatori public, protejat și privat, care denotă, respectiv, membri publici ai unei clase, membrii clasei disponibili în interiorul clasei și din clasele descendente și ascunși, disponibili numai în interiorul clasei. Nomenclatura specifică a modificatorilor și semnificațiile lor exacte diferă de la o limbă la alta. Metode de acces Câmpurile de clasă în cazul general nu ar trebui să fie accesibile din exterior, deoarece un astfel de acces ar permite modificarea arbitrară a stării interne a obiectelor. Prin urmare, câmpurile sunt declarate de obicei ascunse (sau limbajul, în principiu, nu permite accesarea câmpurilor de clasă din exterior), iar pentru accesarea datelor din câmpuri se folosesc metode speciale numite metode accesorii. Astfel de metode fie returnează valoarea acestui sau aceluia câmp, fie scriu o nouă valoare în acest câmp. La scriere, accesorul poate verifica validitatea valorii care se scrie și, dacă este necesar, poate efectua alte manipulări cu datele obiectului pentru ca acestea să rămână corecte (consecvente intern). Metodele de acces se mai numesc accesori (din engleza access - access), iar separat - getters (english get - reading) si setters (english set - writing). Proprietăți ale obiectului Pseudo-Câmpuri, disponibile pentru citire și/sau scriere. Proprietățile arată ca câmpuri în exterior și sunt utilizate în același mod ca și câmpurile disponibile (cu unele excepții), totuși, de fapt, la accesarea lor, sunt apelate metode accesorii. Astfel, proprietățile pot fi considerate câmpuri de date „inteligente” care însoțesc accesul la datele interne ale unui obiect cu orice acțiuni suplimentare (de exemplu, atunci când o modificare a coordonatei unui obiect este însoțită de redesenarea acestuia într-un loc nou). Proprietățile nu sunt, de fapt, nimic mai mult decât zahăr sintactic, deoarece nu adaugă nicio caracteristică nouă, ci doar ascund invocarea metodelor accesorii. Implementarea limbajului specific al proprietăților poate fi diferită. De exemplu, o declarație de proprietate conține direct cod de accesoriu care este apelat numai atunci când se lucrează cu proprietăți, adică nu necesită metode accesorii separate care sunt disponibile pentru invocarea directă. În Delphi, o declarație de proprietate conține doar numele metodelor accesorii care ar trebui apelate la accesarea câmpului. Accesorii în sine sunt metode normale cu unele cerințe suplimentare de semnătură.

Polimorfismul este implementat prin introducerea unor reguli în limbaj, conform cărora unei variabile de tip „clasă” i se poate atribui un obiect al oricărei clase care este descendentă a clasei sale.

Proiectarea programului în general

OOP este axat pe dezvoltarea de sisteme software mari dezvoltate de o echipă de programatori (posibil suficient de mare). Proiectarea sistemului în ansamblu, crearea componentelor individuale și integrarea acestora în produsul final sunt adesea efectuate de diferiți oameni și nu există un singur specialist care să știe totul despre proiect.

Proiectarea orientată pe obiecte se concentrează pe descrierea structurii sistemului proiectat (prioritate în raport cu descrierea comportamentului acestuia, spre deosebire de programarea funcțională), adică, de fapt, ca răspuns la două întrebări principale:

  • Din ce părți este compus sistemul?;
  • Care este responsabilitatea fiecăreia dintre părțile sale.

Alocarea pieselor se face in asa fel incat fiecare sa aiba un volum minim si un set precis definit de functii (datorii) indeplinite, si in acelasi timp sa interactioneze cat mai putin cu celelalte parti.

O rafinare suplimentară duce la selectarea unor fragmente mai mici ale descrierii. Pe măsură ce descrierea este detaliată și se determină responsabilitatea, se identifică datele care trebuie stocate, prezența agenților apropiați în comportament, care devin candidați pentru implementare sub formă de clase cu strămoși comuni. După selectarea componentelor și definirea interfețelor dintre acestea, implementarea fiecărei componente poate fi realizată aproape independent de celelalte (desigur, sub rezerva disciplinei tehnologice corespunzătoare).

Construirea corectă a ierarhiei claselor este de mare importanță. Una dintre problemele cunoscute ale sistemelor mari construite folosind tehnologia OOP este așa-numita problema de fragilitate a clasei de bază... Constă în faptul că, în etapele ulterioare de dezvoltare, atunci când se construiește o ierarhie de clasă și s-a dezvoltat o cantitate mare de cod pe baza acesteia, se dovedește a fi dificil sau chiar imposibil să se facă modificări codului. clasele de bază ale ierarhiei (din care sunt generate toate sau multe dintre clasele care lucrează în sistem). Chiar dacă modificările pe care le faceți nu afectează interfața clasei de bază, modificarea comportamentului acesteia poate afecta clasele descendente în moduri imprevizibile. În cazul unui sistem mare, dezvoltatorul clasei de bază pur și simplu nu este capabil să prezică consecințele modificărilor, nici măcar nu știe cum se utilizează exact clasa de bază și pe ce caracteristici ale comportamentului său funcționarea corectă a descendentului clasele depinde.

Diverse metodologii POO

Programarea componentelor este următoarea etapă în dezvoltarea OOP; prototipul și programarea orientată spre clasă sunt abordări diferite pentru a crea un program care poate fi combinat, având propriile avantaje și dezavantaje.

Programarea componentelor

Programarea orientată pe componente este un fel de „suprastructură” peste OOP, un set de reguli și restricții care vizează construirea de sisteme software mari în dezvoltare, cu o durată de viață lungă. Un sistem software din această metodologie este o colecție de componente cu interfețe bine definite. Modificările la un sistem existent se fac prin crearea de noi componente pentru a le completa sau înlocui pe cele existente. La crearea de noi componente bazate pe cele create anterior, utilizarea moștenirii implementării este interzisă - noua componentă poate moșteni doar interfețele celei de bază. În acest fel, programarea componentelor ocolește problema fragilității clasei de bază.

Programare prototip

Programarea prototipică, păstrând în același timp unele dintre caracteristicile OOP, a abandonat conceptele de bază de clasă și moștenire.

  • Un prototip este un obiect eșantion, în imaginea și asemănarea căruia sunt create alte obiecte. Obiectele copiate pot menține o relație cu obiectul părinte, moștenind automat modificările din prototip; această caracteristică este definită într-o limbă specifică.
  • În loc de un mecanism pentru descrierea claselor și generarea de instanțe, limbajul oferă un mecanism pentru crearea unui obiect (prin specificarea unui set de câmpuri și metode pe care trebuie să le aibă un obiect) și un mecanism pentru clonarea obiectelor.
  • Fiecare obiect nou creat este o „instanță fără clasă”. Fiecare obiect poate deveni prototip- să fie folosit pentru a crea un obiect nou folosind o operație clonarea... După clonare, noul obiect poate fi schimbat, în special, poate fi completat cu noi câmpuri și metode.
  • Obiectul clonat fie devine o copie completă a prototipului, stochând toate valorile câmpurilor sale și duplicând metodele acestuia, fie păstrează o referință la prototip, excluzând câmpurile și metodele clonate până când acestea sunt modificate. În acest din urmă caz, runtime-ul oferă un mecanism delegatii- dacă, la accesarea unui obiect, acesta nu conține el însuși metoda sau câmpul de date solicitat, apelul este trecut la prototip, de la acesta, dacă este necesar, mai departe de-a lungul lanțului.

Programare orientată pe clasă

Programarea orientată spre clasă este o programare centrată pe date în care datele și comportamentul sunt indisolubil legate. Împreună, datele și comportamentul sunt o clasă. În consecință, în limbile bazate pe conceptul de „clasă”, toate obiectele sunt împărțite în două tipuri principale - clase și instanțe. O clasă definește o structură și o funcționalitate (comportament) care este aceeași pentru toate instanțele unei clase date. O instanță este un purtător de date - adică are o stare care se modifică în funcție de comportamentul specificat de clasă. În limbajele orientate spre clasă, o nouă instanță este creată prin apelarea constructorului de clasă (eventual cu un set de parametri). Instanța rezultată are o structură și un comportament care sunt codificate de clasa sa.

Performanța programului obiect

Grady Booch subliniază următoarele motive pentru degradarea performanței programului din cauza utilizării instrumentelor orientate pe obiecte:

Metode de legare dinamică Asigurarea comportamentului polimorf al obiectelor duce la necesitatea de a lega metodele numite de program (adică de a determina ce metodă anume va fi apelată) nu în timpul compilării, ci în timpul execuției programului, ceea ce necesită timp suplimentar. În același timp, este necesară legarea dinamică reală pentru cel mult 20% din apeluri, dar unele limbaje OOP o folosesc în mod constant. Profunzimea considerabilă de abstractizare a dezvoltării OOP duce adesea la crearea de aplicații „stratificate”, în care execuția acțiunii necesare de către un obiect este redusă la o mulțime de apeluri către obiecte de un nivel inferior. Într-o astfel de aplicație, există o mulțime de apeluri de metodă și returnări de metode, care afectează în mod natural performanța. Moștenirea „încețoșează” codul. Codul legat de clasele „finale” ale ierarhiei de moștenire, care sunt de obicei utilizate direct de program, se află nu numai în aceste clase, ci și în clasele lor strămoși. Metodele aparținând aceleiași clase sunt de fapt descrise în clase diferite. Acest lucru duce la două puncte frustrante:

  • Viteza de traducere scade, deoarece linkerul trebuie să încarce descrierile tuturor claselor din ierarhie.
  • Performanța programului într-un sistem cu memorie de pagină scade - deoarece metodele aceleiași clase sunt situate fizic în locuri diferite în cod, departe unele de altele, atunci când fragmentele de program accesează în mod activ metodele moștenite, sistemul este forțat să facă frecvente comutări de pagină.
Încapsularea reduce viteza de acces la date Interzicerea accesului direct la câmpurile de clasă din exterior duce la necesitatea de a crea și de a utiliza metode accesorii. Scrierea, compilarea și executarea metodelor de accesorii implică cheltuieli suplimentare. Crearea dinamică și distrugerea obiectelor Obiectele create dinamic sunt, de regulă, alocate pe heap, ceea ce este mai puțin eficient decât plasarea lor pe stivă și, în plus, alocarea de memorie statică pentru ele în etapa de compilare.

În ciuda dezavantajelor observate, Booch susține că beneficiile utilizării OOP sunt mai semnificative. În plus, o creștere a productivității datorită unei mai bune organizări a codului OOP, potrivit acestuia, compensează în unele cazuri costurile generale suplimentare de organizare a funcționării programului. De asemenea, puteți observa că multe efecte de degradare a performanței pot fi netezite sau chiar eliminate complet prin optimizarea codului de înaltă calitate de către compilator. De exemplu, scăderea menționată mai sus a vitezei de acces la câmpurile de clasă din cauza utilizării metodelor accesorii este eliminată dacă compilatorul folosește substituția inline în loc să apeleze metoda accesorii (compilatorii moderni fac acest lucru destul de sigur).

OOP critică

În ciuda unor remarci critice despre POO, această paradigmă este utilizată în prezent în marea majoritate a proiectelor industriale. Cu toate acestea, OOP nu poate fi considerată cea mai bună tehnică de programare în toate cazurile.

Observații critice despre POO:

  • S-a demonstrat că nu există o diferență semnificativă în productivitatea dezvoltării software între OOP și abordarea procedurală.
  • Christopher Date subliniază imposibilitatea de a compara OOP și alte tehnologii, în mare parte din cauza lipsei unei definiții stricte și general acceptate a OOP.
  • Alexander Stepanov, într-unul dintre interviurile sale, a subliniat că OOP este „greșit din punct de vedere metodologic” și că „... OOP este practic aceeași farsă ca inteligența artificială...”.
  • Frederick Brooks subliniază că partea cea mai dificilă a creării de software este „... specificarea, proiectarea și testarea constructelor conceptuale, nu munca de exprimare a acestor constructe conceptuale...”. OOP (împreună cu tehnologii precum inteligența artificială, verificarea programelor, programarea automată, programarea grafică, sistemele expert etc.), în opinia sa, nu este un „glonț de argint” care ar putea reduce complexitatea dezvoltării sistemelor software cu un ordin de magnitudinea. Potrivit lui Brooks, „... OOP reduce doar complexitatea care este introdusă în expresia designului. Designul rămâne complex în natură...”.
  • Edsger Dijkstra a subliniat: „... ceea ce societatea cere în majoritatea cazurilor este un elixir pentru toate bolile. Desigur, „Elixir” are nume foarte impresionante, altfel va fi foarte greu să vindeți ceva: „Analiză și proiectare structurală”, „Inginerie software”, „Modele de maturitate”, „Sisteme informaționale de management” (Sisteme informaționale de management), „Integrated”. Suport proiecte medii "," Orientare obiect "," Reinginerie proces de afaceri "...".
  • Niklaus Wirth consideră că POO nu este altceva decât o suprastructură banală asupra programării structurate, iar exagerarea importanței acesteia, exprimată, printre altele, prin includerea în limbajele de programare a instrumentelor din ce în ce mai la modă „orientate pe obiecte”, dăunează calității. a software-ului în curs de dezvoltare.
  • Patrick Killelia a scris în cartea sa „Tuning a Web Server”: „... OOP vă oferă multe modalități de a încetini programele...”.
  • Un binecunoscut articol de prezentare generală despre problemele programării moderne OOP listează câteva probleme tipice OOP [ ] .
  • În folclorul de programare, critica abordării orientate pe obiecte versus abordarea funcțională folosind metafora „ Regatele substantivelor„Din un eseu de Steve Yeggie.

Dacă încercăm să clasificăm criticile la OOP, putem distinge mai multe aspecte ale criticii acestei abordări a programării.

Critica reclamelor OOP Criticează în mod explicit sau implicit în scrierile unor propagandişti OOP, precum şi în materialele publicitare pentru instrumente de dezvoltare „orientate pe obiecte”, ideea programării obiectelor ca un fel de abordare omnipotentă care elimină prin magie complexitatea programare. După cum au observat mulți, inclusiv Brooks și Dijkstra menționate mai sus, „nu există niciun glonț de argint” - indiferent de paradigma de programare la care aderă dezvoltatorul, crearea unui sistem software complex non-trivial implică întotdeauna o investiție semnificativă de resurse intelectuale și timp. Dintre cei mai calificați specialiști OOP, nimeni, de regulă, nu neagă validitatea acestui tip de critică. Contestarea eficienței dezvoltării POO Criticii contestă teza că dezvoltarea de programe orientate pe obiect necesită mai puține resurse sau duce la crearea unui software mai bun. Se realizează o comparație a costurilor de dezvoltare prin diferite metode, pe baza căreia se concluzionează că OOP nu are avantaje în această direcție. Având în vedere dificultatea extremă de a compara în mod obiectiv diferite modele, astfel de comparații sunt cel puțin controversate. Pe de altă parte, se dovedește că afirmațiile despre eficacitatea OOP sunt la fel de controversate. Performanța programelor orientate pe obiect Se subliniază că o serie de „trăsături înnăscute” ale tehnologiei OOP fac ca programele bazate pe aceasta să fie mai puțin eficiente din punct de vedere tehnic decât programele similare non-obiect. Fără a nega costurile generale suplimentare reale de organizare a funcționării programelor OOP (a se vedea secțiunea „Performanță” de mai sus), trebuie remarcat, totuși, că importanța degradării performanței este adesea exagerată de critici. În condițiile moderne, când capacitățile tehnice ale computerelor sunt extrem de mari și în continuă creștere, pentru majoritatea programelor de aplicație, eficiența tehnică este mai puțin importantă decât funcționalitatea, viteza de dezvoltare și mentenabilitatea. Doar pentru o clasă foarte limitată de software (software de sisteme încorporate, drivere de dispozitiv, software de sistem de nivel scăzut, software științific) performanța rămâne un factor critic. Critica soluțiilor tehnologice individuale în limbaje și biblioteci OOP Această critică este numeroase, dar nu afectează OOP ca atare, ci acceptabilitatea și aplicabilitatea în cazuri specifice a anumitor implementări ale mecanismelor sale. Unul dintre obiectele preferate de critică este limbajul C++, care este unul dintre cele mai comune limbaje industriale OOP.

Limbaje orientate pe obiecte

Multe limbaje moderne sunt concepute special pentru a facilita programarea orientată pe obiecte. Totuși, trebuie remarcat faptul că este posibil să se aplice tehnici OOP pentru un limbaj neorientat pe obiect și invers, utilizarea unui limbaj orientat pe obiect nu înseamnă că codul devine automat orientat pe obiect.

De obicei, un limbaj orientat pe obiecte (OOL) conține următorul set de elemente:

  • Declarația claselor cu câmpuri (date - membrii clasei) și metode (funcții - membrii clasei).
  • Mecanismul de extindere a unei clase (moștenire) este generarea unei noi clase dintr-o clasă existentă, cu includerea automată a tuturor caracteristicilor de implementare ale clasei strămoși în componența clasei descendente. Majoritatea OOE acceptă doar moștenirea unică.
  • Variabile polimorfe și parametri ai funcțiilor (metodelor) care vă permit să atribuiți instanțe ale diferitelor clase aceleiași variabile.
  • Comportamentul polimorf al instanțelor de clasă prin utilizarea metodelor virtuale. În unele OOL, toate metodele de clasă sunt virtuale.

Unele limbi adaugă anumite instrumente suplimentare la setul minim specificat. Printre ei:

  • Constructori, destructori, finalizatori;
  • Proprietăți (accesorii);
  • Indexoare;
  • Controale pentru vizibilitatea componentelor clasei (interfețe sau modificatori de acces, cum ar fi public, privat, protejat, caracteristică etc.).

Unele limbi respectă pe deplin principiile OOP - în ele toate elementele principale sunt obiecte cu stare și metode asociate. Exemple de astfel de limbi sunt Smalltalk, Eiffel. Există limbaje hibride care combină subsistemul obiect într-o formă integrală cu subsistemele altor paradigme ca „două sau mai multe limbaje într-una”, care permit combinarea modelelor de obiecte cu altele într-un singur program și estomparea liniei dintre obiect. -orientate și alte paradigme datorită capabilităților non-standard care echilibrează între OOP și alte paradigme (cum ar fi dispecerare multiplă, clase parametrice, capacitatea de a manipula metodele de clasă ca obiecte independente etc.). Exemple de astfel de limbi:

Clasa (clase) este un tip de date definit de utilizator. Clasa definește proprietățile și comportamentul unui obiect sau proces sub formă de câmpuri de date și funcții pentru lucrul cu acestea.

O proprietate esențială a unei clase este că detaliile implementării acesteia sunt ascunse utilizatorilor clasei din spatele interfeței. Astfel, o clasă ca model al unui obiect din lumea reală este o cutie neagră, închisă în raport cu lumea exterioară.

Ideea din spatele claselor este nucleul programării orientate pe obiecte (OOP). Principiile de bază ale OOP au fost dezvoltate în limbajele Simula-67 și SmallTalk, dar la acel moment nu s-au răspândit din cauza dificultăților de stăpânire și a eficienței scăzute de implementare.

Sunt denumite valori specifice tipului de date „clasă”. instanțe de clasă sau obiecte (obiecte) .

Sunt apelate subrutinele care definesc operații asupra obiectelor de clasă metode (metode). Apelurile de metodă sunt apelate mesaje (mesaje). Întregul set de metode de pe un obiect se numește protocolul mesajului sau interfata de mesaje (mesajinterfata) obiect. Mesajul trebuie să aibă cel puțin două părți: obiectul specific căruia ar trebui să fie trimis și numele metodei care definește acțiunea necesară asupra obiectului. Astfel, calculul într-un program orientat pe obiecte este determinat de mesajele transmise de la un obiect la altul.

Obiectele interacționează între ele prin trimiterea și primirea de mesaje. Un mesaj este o cerere de acțiune care conține un set de parametri necesari. Mecanismul de mesaje este implementat prin apelarea funcțiilor corespunzătoare. Cu ajutorul OOP, așa-numitul model bazat pe evenimente este ușor de implementat, atunci când datele sunt active și controlează apelul unei anumite bucăți de cod de program.

OOP este o tehnică de programare care dezvoltă principiile programării structurate și se bazează pe următoarele abstracții de date:

eu. Încapsulare : combinarea datelor cu proceduri și funcții într-un singur bloc de cod de program (datele și metodele de lucru cu acestea sunt considerate câmpuri ale unui obiect).

II. Moştenire - transfer de metode și proprietăți de la strămoș la descendent, fără a fi nevoie de a scrie cod suplimentar de program (prezența instanțelor de clasă; descendenți, bunici, ierarhie).

III. Polimorfismul - capacitatea de a schimba proprietățile și comportamentul obiectelor care au sens identic, în funcție de tipul lor (un singur nume pentru o anumită acțiune, care se desfășoară în moduri diferite pentru obiectele ierarhiei).

Încapsulare

Pentru prima dată, conceptul de încapsulare a fost folosit în limbaje care sprijină așa-numita abordare abstractă a programării (de exemplu, Modula-2). Ideea principală a abordării abstracte este de a ascunde de utilizator structura informațiilor despre obiect, pentru a-i permite să primească datele necesare pentru a lucra cu obiectul numai prin proceduri legate de acest obiect. Această tehnică poate îmbunătăți semnificativ fiabilitatea și portabilitatea software-ului dezvoltat. Fiabilitatea este crescută datorită faptului că toate procedurile de lucru cu date obiect sunt relativ simple și transparente, ceea ce înseamnă că pot fi dezvoltate mai eficient. La modificarea structurii datelor, este suficient să reluați numai programele direct legate de obiect, iar programele mai complexe care utilizează acest obiect nu trebuie modificate. Această împrejurare crește atât fiabilitatea, cât și mobilitatea programelor create.

Moştenire

În a doua jumătate a anilor 1980, pentru mulți dezvoltatori de software a devenit evident că una dintre cele mai bune oportunități de creștere a productivității a fost reutilizarea programelor. Este destul de evident că tipurile de date abstracte, cu încapsularea și controlul accesului lor, trebuie reutilizate din nou și din nou. Problema cu reutilizarea tipurilor de date abstracte în aproape toate cazurile este că proprietățile și capacitățile tipurilor existente nu sunt potrivite pentru noile utilizări. Tipurile vechi trebuie modificate cel puțin minim. Astfel de modificări pot fi dificil de implementat și necesită ca o persoană să înțeleagă o parte, dacă nu tot, din codul existent. În plus, în multe cazuri, modificările implică modificări în toate programele client.

A doua problemă cu programarea orientată către date este că toate definițiile tipurilor de date abstracte sunt independente și la același nivel al ierarhiei. Acest lucru împiedică adesea structura programului pentru a se potrivi zonei sale cu probleme. În multe cazuri, problema inițială conține categorii de obiecte interconectate care sunt atât moștenitori ai acelorași strămoși (adică, situati la același nivel al ierarhiei), cât și strămoși și moștenitori (adică aparținând împreună unei anumite subordonări).

Moștenirea rezolvă atât problemele de modificare care decurg din reutilizarea unui tip de date abstracte, cât și problemele de organizare a programului. Dacă un nou tip de date abstracte poate moșteni datele și proprietățile funcționale ale unui tip existent, precum și poate modifica unele dintre aceste entități și poate adăuga entități noi, atunci reutilizarea este mult facilitată fără a fi nevoie de a face modificări tipului de date abstracte reutilizabile. Programatorii pot lua un tip de date abstracte existente și pot modela un nou tip pe baza acestuia pentru a îndeplini noile cerințe ale sarcinii. Să presupunem că programul dumneavoastră are un tip de date abstract pentru matrice de numere întregi care include o operație de sortare. După o anumită perioadă de utilizare, programul este modificat și necesită nu doar un tip de date abstract pentru tablouri de numere întregi cu operație de sortare, ci și o operație de calcul a mediei aritmetice pentru elementele obiectelor care sunt tablouri. Deoarece structura matricei este ascunsă într-un tip de date abstract, fără moștenire, acest tip trebuie modificat prin adăugarea unei noi operații la această structură. Cu moștenirea, nu este nevoie să modificați un tip existent; puteți descrie o subclasă de tip existent care suportă nu numai operația de sortare, ci și operația de calculare a mediei aritmetice.

O clasă care este definită prin moștenirea de la o altă clasă este numită clasa derivata (derivatclasă) , sau subclasa (subclasă) ... Se numește clasa din care este derivată noua clasă clasa de părinte (mamăclasă) , sau superclasa (superclasă) .

În cel mai simplu caz, clasa moștenește toate entitățile (variabilele și metodele) clasei părinte. Această moștenire poate fi complicată prin introducerea controlului accesului la entitățile clasei părinte.

Acest control de acces permite programatorului să ascundă părți ale unui tip de date abstract de la clienți. Acest control al accesului se găsește de obicei în clasele de limbă OO. Clasele derivate sunt un alt tip de client care poate fi fie acordat, fie refuzat. Pentru a face față acestui lucru, unele limbaje orientate pe obiecte includ o a treia categorie de control al accesului, denumită adesea protejat, care este folosită pentru a acorda acces la clasele derivate și a refuza accesul la alte clase.

Pe lângă entitățile moștenite, o clasă derivată poate adăuga entități noi și poate modifica metode. O metodă modificată are același nume și adesea același protocol ca metoda pe care o modifică. Se spune că noua metodă suprascrie versiunea moștenită a metodei, care se numește, prin urmare, metoda overriden. Cel mai comun scop al unei metode suprascrise este de a efectua o operație care este specifică obiectelor unei clase derivate și nu este comună obiectelor clasei părinte.

Dezvoltarea unui program pentru un sistem orientat pe obiecte începe cu definirea unei ierarhii de clasă care descrie relațiile dintre obiectele care vor fi incluse în programul care rezolvă problema. Cu cât această ierarhie de clasă se potrivește mai bine cu partea problematică, cu atât soluția completă va fi mai naturală.

Dezavantajul moștenirii ca mijloc de a face codul mai ușor de reutilizat este că creează dependențe între clasele din ierarhia moștenirii. Acest lucru diminuează unul dintre avantajele tipurilor de date abstracte, și anume că sunt independente reciproc. Desigur, nu toate tipurile de date abstracte trebuie să fie complet independente, dar, în general, independența tipurilor de date abstracte este una dintre cele mai puternice proprietăți pozitive ale acestora. Cu toate acestea, creșterea reutilizabilității tipurilor de date abstracte fără a crea dependențe între unele dintre ele poate fi dificilă, dacă nu complet fără speranță.

Polimorfismul

A treia proprietate a limbajelor de programare orientate pe obiect este tipul de polimorfism oferit de mesajele de legare dinamică la definițiile metodelor. Această proprietate este menținută prin permiterea definirii variabilelor polimorfe de tipul clasei părinte, care se pot referi și la obiecte din orice subclase ale clasei date. O clasă părinte poate defini o metodă care să fie suprascrisă în subclasele sale. Operațiile definite prin aceste metode sunt similare, dar trebuie rafinate pentru fiecare clasă din ierarhie. Când o astfel de metodă este apelată printr-o variabilă polimorfă, acel apel este legat dinamic de metoda din clasa corespunzătoare. Unul dintre obiectivele legăturii dinamice este de a face sistemele software mai ușor de extins pe măsură ce sunt dezvoltate și întreținute. Astfel de programe pot fi scrise pentru a opera pe obiecte din clase personalizate. Aceste operațiuni sunt personalizabile în sensul că pot fi aplicate obiectelor din orice clasă care derivă din aceeași clasă de bază.

Calcularea în limbaje orientate pe obiecte

Toate calculele într-un limbaj complet orientat pe obiecte se fac prin transmiterea unui mesaj unui obiect pentru a apela una dintre metodele acestuia. Răspunsul la mesaj este un obiect care returnează rezultatul calculelor efectuate prin această metodă. Executarea unui program într-un limbaj orientat pe obiecte poate fi descrisă ca simulând un set de calculatoare (obiecte) care interacționează între ele prin schimbul de mesaje. Fiecare obiect este o abstractizare a unui computer, în sensul că stochează date și asigură execuția proceselor pentru a manipula acele date. În plus, obiectele pot trimite și primi mesaje. În esență, acestea sunt proprietățile de bază ale unui computer - să stocheze și să proceseze date, precum și să trimită și să primească mesaje.

Esența programării orientate pe obiecte este de a rezolva probleme prin identificarea obiectelor reale corespunzătoare și a procesării necesare acestor obiecte; și modelarea ulterioară a acestor obiecte, procesele lor și conexiunile necesare între ele.

Biblioteca de componente vizuale (VCL)

Delphi conține un număr mare de clase destinate dezvoltării rapide a aplicațiilor. Biblioteca este scrisă în Object Pascal și are o conexiune directă cu IDE-ul Delphi.

Toate clasele VCL sunt situate la un anumit nivel al ierarhiei și formei arborele (ierarhia) claselor.

Cunoașterea originii unui obiect este de mare ajutor în a învăța despre acesta, deoarece copilul moștenește toate elementele obiectului părinte. Deci, dacă proprietatea Caption aparține clasei TControl, atunci descendenții săi vor avea și această proprietate, de exemplu, clasele TButton și TCheckBox și componentele - caseta de selectare Button și, respectiv, CheckBox. Un fragment al ierarhiei claselor cu cele mai importante clase este prezentat în Fig.

Pe lângă ierarhia claselor, codurile sursă ale modulelor, care se află în directorul SOURCE al directorului principal Delphi, sunt de mare ajutor în învățarea sistemului de programare.

Din voia sorții, trebuie să citesc un curs special despre modele de design la universitate. Cursul special este obligatoriu, prin urmare, studenții vin la mine foarte diferit. Desigur, printre ei sunt și programatori practicanți. Dar, din păcate, majoritatea oamenilor au dificultăți în înțelegerea termenilor de bază ai POO.

Pentru a face acest lucru, am încercat să explic conceptele de bază ale POO (clasă, obiect, interfață, abstractizare, încapsulare, moștenire și polimorfism) folosind exemple mai mult sau mai puțin vii.

Prima parte de mai jos este despre clase, obiecte și interfețe.
A doua parte ilustrează încapsularea, polimorfismul și moștenirea

Concepte de bază ale POO

Clasă
Imaginați-vă că proiectați o mașină. Știți că o mașină trebuie să conțină motor, suspensie, două faruri, 4 roți etc. De asemenea, știți că mașina dvs. trebuie să poată ridica și decelera, întoarce și inversează. Și, cel mai important, știi exact cum interacționează motorul și roțile, după ce legi se mișcă arborele cu came și arborele cotit, precum și cum sunt aranjate diferențialele. Sunteți încrezător în cunoștințele dvs. și începeți să proiectați.

Descrieți toate părțile care compun vehiculul dvs. și modul în care aceste părți interacționează între ele. În plus, descrieți ce trebuie să facă utilizatorul pentru a face mașina să frâneze sau pentru a aprinde farurile pentru faza lungă. Rezultatul muncii tale va fi o schiță. Tocmai ai dezvoltat ceea ce numește OOP Clasă.

Clasă Este o modalitate de a descrie o entitate, de a defini starea si comportamentul in functie de aceasta stare, precum si regulile de interactiune cu aceasta entitate (contract).

Din punct de vedere al programării, o clasă poate fi privită ca un set de date (câmpuri, atribute, membri ai clasei) și funcții de lucru cu acestea (metode).

Din punctul de vedere al structurii programului, o clasă este un tip de date complex.

În cazul nostru, clasa va reprezenta entitatea - mașina. Atributele clasei vor fi motor, suspensie, caroserie, patru roți etc. Metodele clasei vor fi „deschideți ușa”, „apăsați pedala de accelerație” și, de asemenea, „pompați o porțiune de benzină din rezervorul de benzină în motor”. Primele două metode sunt disponibile pentru execuție de către alte clase (în special, clasa „Driver”). Acesta din urmă descrie interacțiunile din cadrul clasei și nu este disponibil pentru utilizator.

În viitor, în ciuda faptului că cuvântul „utilizator” este asociat cu Klondike Solitaire și Microsoft Word, ne vom referi ca utilizatori acei programatori care vă folosesc clasa, inclusiv dvs. Persoana care este autorul clasei, vom numi dezvoltatorul.

Un obiect
Ați făcut o treabă grozavă și mașinile proiectate conform desenelor dvs. ies de pe linia de asamblare. Iată-i, stând în rânduri egale în curtea fabricii. Fiecare dintre ele reproduce exact desenele tale. Toate sistemele interacționează exact așa cum ați proiectat. Dar fiecare mașină este unică. Toate au un număr de caroserie și motor, dar toate aceste numere sunt diferite, mașinile diferă ca culoare, iar unele chiar au piese turnate în loc de discuri ștanțate. Aceste mașini sunt în esență obiecte din clasa ta.

Obiect (instanță) Este un membru separat al unei clase cu o stare și un comportament specific care sunt complet definite de clasă.

În termeni simpli, un obiect are valori specifice de atribut și metode care operează pe aceste valori pe baza regulilor definite în clasă. În acest exemplu, dacă o clasă este o mașină abstractă din „lumea ideilor”, atunci obiectul este o anumită mașină care stă sub geamurile tale.

Interfață
Când mergem la un aparat de cafea sau ne urcăm la volan, începem să interacționăm cu ei. De obicei, interacțiunea are loc cu ajutorul unui anumit set de elemente: un slot pentru acceptarea monedelor, un buton pentru selectarea unei băuturi și un compartiment pentru distribuirea unui pahar într-un aparat de cafea; volan, pedale, schimbător de viteze în mașină. Există întotdeauna un set limitat de comenzi cu care putem interacționa.

Interfață Este o colecție de metode de clasă disponibile pentru utilizare de către alte clase.

Evident, interfața unei clase va fi un set de toate metodele sale publice împreună cu un set de atribute publice. Practic, o interfață specifică o clasă, definind clar toate acțiunile posibile asupra acesteia.
Un bun exemplu de interfață ar fi un tablou de bord al mașinii, care vă permite să invocați metode precum accelerarea, frânarea, virajul, schimbarea vitezelor, aprinderea farurilor etc. Adică toate acțiunile pe care o altă clasă (în cazul nostru, șoferul) le poate efectua atunci când interacționează cu mașina.

Când descrieți o interfață de clasă, este foarte important să găsiți un echilibru între flexibilitate și simplitate. O clasă cu o interfață simplă va fi ușor de utilizat, dar vor apărea probleme pe care nu le va putea rezolva cu ajutorul acesteia. În același timp, dacă interfața este flexibilă, atunci, cel mai probabil, va consta din metode destul de complexe, cu un număr mare de parametri, care vă vor permite să faceți multe, dar utilizarea acesteia va fi plină de dificultăți mari și de riscuri. de a face o greșeală, ceva confuz.

Un exemplu de interfață simplă este o mașină cu transmisie automată. Orice blondă care a absolvit un curs de conducere de două săptămâni va putea să-și stăpânească gestionarea foarte repede. Pe de altă parte, este nevoie de câteva luni sau chiar ani de antrenament intens pentru a stăpâni controlul unei aeronave moderne de pasageri. Nu mi-ar plăcea să mă aflu la bordul unui Boeing operat de un bărbat cu două săptămâni de experiență în zbor. Pe de altă parte, nu veți forța niciodată o mașină să decoleze și să zboare de la Moscova la Washington.

Java este un limbaj orientat obiect. Aceasta înseamnă că trebuie să scrieți programe Java folosind un stil orientat pe obiecte. Și acest stil se bazează pe utilizarea obiectelor și claselor în program. Să încercăm cu ajutorul exemplelor să ne dăm seama ce sunt clasele și obiectele, precum și cum să aplicăm în practică principiile de bază ale POO: abstracție, moștenire, polimorfism și încapsulare.

Ce este un obiect?

Lumea în care trăim este formată din obiecte. Dacă ne uităm în jur, vom vedea că suntem înconjurați de case, copaci, mașini, mobilier, vase, calculatoare. Toate aceste articole sunt obiecte și fiecare dintre ele are un set de caracteristici specifice, comportament și scop. Suntem obișnuiți cu obiecte și le folosim întotdeauna în scopuri foarte specifice. De exemplu, dacă trebuie să ajungem la muncă, folosim mașina, dacă vrem să mâncăm, folosim vase, iar dacă ne odihnim, avem nevoie de o canapea confortabilă. O persoană este obișnuită să gândească obiectiv pentru a rezolva problemele din viața de zi cu zi. Acesta a fost unul dintre motivele utilizării obiectelor în programare, iar această abordare a creării de programe a fost numită orientată pe obiecte. Să dăm un exemplu. Imaginați-vă că ați dezvoltat un nou model de telefon și doriți să începeți producția în masă a acestuia. În calitate de dezvoltator al unui telefon, știi pentru ce este, cum va funcționa și din ce părți va consta (corp, microfon, difuzor, fire, butoane etc.). În plus, doar tu știi cum să conectezi aceste părți. Cu toate acestea, nu aveți de gând să fabricați telefoane personal, pentru aceasta aveți un întreg personal de muncitori. Pentru a nu trebui să explicați de fiecare dată cum să conectați părțile telefonului și pentru ca toate telefoanele să fie la fel în timpul producției, înainte de a începe să le produceți, trebuie să faceți un desen sub forma unei descrieri a dispozitivul telefonic. În OOP, o astfel de descriere, desen, diagramă sau șablon se numește o clasă din care este creat un obiect atunci când programul este executat. O clasă este o descriere a unui obiect care nu a fost încă creat, ca un șablon general, constând din câmpuri, metode și un constructor, iar un obiect este o instanță a unei clase create pe baza acestei descrieri.

Abstracția

Să ne gândim acum cum putem trece de la un obiect din lumea reală la un obiect din program folosind exemplul unui telefon. Istoria acestui mijloc de comunicare depaseste 100 de ani iar telefonul modern, spre deosebire de predecesorul sau din secolul al XIX-lea, este un aparat mult mai complex. Când folosim un telefon, nu ne gândim la structura lui și la procesele care au loc în interiorul acestuia. Folosim doar funcțiile oferite de designerii de telefoane - butoane sau ecran tactil pentru a selecta un număr și a efectua apeluri. Una dintre cele mai vechi interfețe de telefon a fost un buton care trebuia rotit pentru a efectua un apel. Desigur, acest lucru nu a fost foarte convenabil. Cu toate acestea, mânerul și-a îndeplinit în mod regulat funcția. Dacă te uiți la cel mai modern și chiar primul telefon, poți evidenția imediat cele mai importante detalii care sunt importante atât pentru un dispozitiv de la sfârșitul secolului al XIX-lea, cât și pentru un smartphone ultramodern. Aceasta înseamnă efectuarea unui apel (apelarea unui număr) și primirea unui apel. Acesta este, în esență, ceea ce face un telefon un telefon și nu altceva. Acum am aplicat principiul în OOP - evidențierea celor mai importante caracteristici și informații despre un obiect. Acest principiu se numește abstractizare. Abstracția în POO poate fi definită și ca o modalitate de a reprezenta elementele sarcinii din lumea reală ca obiecte într-un program. Abstracția este întotdeauna asociată cu generalizarea unor informații despre proprietățile obiectelor sau obiectelor, deci principalul este să se separe informațiile semnificative de cele nesemnificative în contextul problemei care se rezolvă. Mai mult, pot exista mai multe niveluri de abstractizare. Să încercăm să aplicăm principiul abstracției pe telefoanele noastre. Pentru început, să evidențiem cele mai comune tipuri de telefoane de la primul până în prezent. De exemplu, ele pot fi reprezentate sub forma unei diagrame prezentate în figura 1. Acum, cu ajutorul abstractizării, putem distinge informații generale în această ierarhie a obiectelor: un tip general abstract de obiecte - un telefon, o caracteristică generală. a unui telefon - anul creării sale și o interfață comună - toate telefoanele sunt capabile să efectueze și să primească apeluri. Așa arată în Java: clasă abstractă publică AbstractPhone (private int year; public AbstractPhone (int year) (this. Year = year;) public abstract void call (int outputNumber); public abstract void ring (int inputNumber);) Pe baza acestei clase abstracte, vom putea crea noi tipuri de telefoane în program folosind alte principii Java OOP de bază, pe care le vom lua în considerare mai jos.

Încapsulare

Prin intermediul abstracții scoatem in evidenta general pentru toate obiectele. Cu toate acestea, fiecare model de telefon este individual și oarecum diferit de celelalte. Cum putem trasa limite în program și defini această individualitate? Cum să ne asigurăm că niciunul dintre utilizatori nu ne poate sparge telefonul accidental sau intenționat sau nu poate încerca să refacă un model în altul? Pentru lumea obiectelor reale, răspunsul este evident: trebuie să pui toate detaliile în husa telefonului. La urma urmei, dacă nu faceți acest lucru și lăsați toate interiorul telefonului și firele care le conectează în exterior, cu siguranță va exista un experimentator curios care dorește să „îmbunătățească” funcționarea telefonului nostru. Pentru a exclude o astfel de interferență cu proiectarea și funcționarea unui obiect în OOP, se utilizează principiul încapsulării - un alt principiu de bază al OOP, în care atributele și comportamentul unui obiect sunt combinate într-o singură clasă, implementarea internă a obiectului este ascuns utilizatorului și este prevăzută o interfață deschisă pentru lucrul cu obiectul. Sarcina programatorului este de a determina ce atribute și metode vor fi disponibile pentru acces public și care sunt implementarea internă a obiectului și ar trebui să fie inaccesibile pentru modificări.

Încapsulare și control acces

De exemplu, în timpul producției, pe spatele telefonului sunt gravate informații despre acesta: anul lansării sau sigla companiei producătorului. Aceste informații caracterizează în mod specific acest model - starea acestuia. Putem spune că dezvoltatorul telefonului s-a ocupat de invariabilitatea acestor informații - aproape nimeni nu s-ar gândi să elimine gravura. În lumea Java, starea obiectelor viitoare este descrisă într-o clasă folosind câmpuri, iar comportamentul lor este descris folosind metode. Posibilitatea de a schimba starea și comportamentul se realizează folosind modificatori de acces la câmpuri și metode - privat, protejat, public , precum și Mod implicit (acces implicit). De exemplu, am decis că anul creării, numele producătorului telefonului și una dintre metode aparțin implementării interne a clasei și nu pot fi modificate de alte obiecte din program. Folosind cod, clasa poate fi descrisă după cum urmează: clasă publică SomePhone (private int year; private String company; public SomePhone (int year, String company) (this. Year = year; this. Company = company;) private void openConnection ( ) (/ / findComutator // openNewConnection ...) public void call () (openConnection (); System.out.println ("Sunt numărul");) public void ring () (System.out.println ("Tink-tink") ;)) Modificator privat face disponibile câmpurile și metodele unei clase numai în cadrul clasei date. Aceasta înseamnă că puteți accesa privat câmpuri din exterior este imposibil, așa cum nu există nicio modalitate de a suna privat metode. Ascunderea accesului la metoda openConnection ne lasă, de asemenea, liber să schimbăm implementarea internă a acestei metode, deoarece această metodă este garantată să nu fie utilizată de alte obiecte și nu le va rupe munca. Pentru a lucra cu obiectul nostru, lăsăm deschise metodele de apel și apel folosind modificatorul public . Furnizarea de metode publice de lucru cu un obiect este, de asemenea, parte a mecanismului de încapsulare, ca și cum ați închide complet accesul la obiect - va deveni inutil.

Moştenire

Să aruncăm o altă privire la diagrama telefonului. Puteți observa că este o ierarhie, în care modelul aflat mai jos are toate caracteristicile modelelor situate mai sus în ramură, plus propriile sale. De exemplu, un smartphone folosește o rețea celulară pentru comunicare (are proprietățile unui telefon mobil), este wireless și portabil (are proprietățile unui telefon fără fir) și poate primi și efectua apeluri (are proprietățile unui telefon). În acest caz, putem vorbi despre moștenirea proprietăților obiectului. În programare, moștenirea se referă la utilizarea claselor existente pentru a le descrie pe altele noi. Să luăm în considerare un exemplu de creare a unei clase de smartphone folosind moștenirea. Toate telefoanele fără fir funcționează cu baterii reîncărcabile, care au o anumită durată de funcționare în ore. Deci, să adăugăm această proprietate la clasa de telefon fără fir: public abstract class WirelessPhone extinde AbstractPhone (private int hour; public WirelessPhone (int year, int hour) (super (an); this. Hour = hour;)) Telefoanele mobile moștenesc proprietățile a unui telefon fără fir, am adăugat și metode de apel și apel la această clasă: public class CellPhone extinde WirelessPhone (public CellPhone (int year, int hour) (super (an, or);) @Override public void call (int outputNumber) ( System.out.println ("Număr apelant" + outputNumber);) @Override public void ring (int inputNumber) (System.out. println ( „Te sună un abonat”+ inputNumber); )) Și în sfârșit, clasa de smartphone, care, spre deosebire de telefoanele mobile clasice, are un sistem de operare cu drepturi depline. Puteți adăuga noi programe suportate de acest sistem de operare pe smartphone-ul dvs., extinzându-i astfel funcționalitatea. Folosind cod, clasa poate fi descrisă după cum urmează: public class Smartphone extinde CellPhone (private String operationSystem; public Smartphone (int year, int hour, String operationSystem) (super (an, oră); this. OperationSystem = operationSystem;) public void install (Program String) (System. out. println ("I install" + program + "for" + operationSystem);)) După cum puteți vedea, am creat destul de mult cod nou pentru a descrie clasa Smartphone, dar am primit o nouă clasă cu noi funcționalități. Utilizarea acestui principiu de OOP java vă permite să reduceți semnificativ cantitatea de cod și, prin urmare, să facilitați munca programatorului.

Polimorfismul

Dacă ne uităm la toate modelele de telefoane, atunci, în ciuda diferențelor de aspect și structura modelelor, putem distinge un comportament comun între ele - toți pot primi și efectua apeluri și au un set destul de clar și simplu de butoane de control. Aplicând unul dintre principiile de bază ale POO, pe care îl cunoaștem deja, abstractizarea în termeni de programare, putem spune că obiectul telefon are o interfață comună. Prin urmare, utilizatorii de telefoane pot folosi destul de confortabil modele diferite, folosind aceleași butoane de control (mecanice sau tactile), fără a intra în complexitățile tehnice ale dispozitivului. Deci, utilizați în mod constant un telefon mobil și puteți efectua cu ușurință un apel de la omologul său fix. Principiul în OOP, când un program poate folosi obiecte cu aceeași interfață fără informații despre structura internă a obiectului, se numește polimorfism ... Să ne imaginăm că în programul nostru trebuie să descriem un utilizator care poate folosi orice model de telefon pentru a apela un alt utilizator. Iată cum o puteți face: public class User (nume String privat; utilizator public (nume String) (acest. Nume = nume;) public void callAnotherUser (număr int, telefon AbstractPhone) ( // aici este polimorfism - folosind telefonul de tip abstract AbstractPhone în cod! telefon. apel (număr); ))) Acum să descriem diferitele modele de telefon. Unul dintre primele modele de telefon: clasa publică ThomasEdisonPhone extinde AbstractPhone (ThomasEdisonPhone public (înt year) (super (an);) @Override public void call (int outputNumber) (System. Out.println ("Rotiți mânerul"); System . Out . println ( „Dați numărul de abonat, domnule”); ) @Override public void ring (int inputNumber) (System. Out. Println ("Telefonul sună");)) Telefon fix comun: public class Phone extinde AbstractPhone (Telefon public (int year) (super (an);) @Override public void call (int outputNumber) (System. out.println ("Sunt numărul" + outputNumber);) @Override public void ring (int inputNumber) (System. out.println ("Telefonul sună" );)) Și, în sfârșit, videofonul tare: VideoPhone public de clasă extinde AbstractPhone (VideoPhone public (înt year) (super (an);) @Override public void call (int outputNumber) (System.out.println ( „Conectez canalul video pentru abonat”+ outputNumber); ) @Override public void ring (int inputNumber) (System.out.println ( „Aveți un apel video primit...”+ inputNumber); )) Să creăm obiecte în metoda main () și să testăm metoda callAnotherUser: AbstractPhone firstPhone = new ThomasEdisonPhone (1879); AbstractPhone phone = nou Telefon (1984); AbstractPhone videoPhone = nou VideoPhone (2018); Utilizator utilizator = utilizator nou ("Andrey"); utilizator. callAnotherUser (224466, primul telefon); // Rotiți mânerul // Dați numărul de abonat, domnule utilizator. callAnotherUser (224466, telefon); // Număr de apel 224466 utilizator. callAnotherUser (224466, videoPhone); // Conectez canalul video pentru abonatul 224466 Apelând aceeași metodă pe obiectul utilizator, am obținut rezultate diferite. Alegerea unei implementări specifice a metodei call în cadrul metodei callAnotherUser a fost făcută dinamic pe baza tipului specific al obiectului apelant în timpul execuției programului. Acesta este principalul avantaj al polimorfismului - alegerea implementării în timpul execuției programului. În exemplele de clasă de telefon de mai sus, am folosit suprascrierea metodei, o tehnică care modifică implementarea unei metode definite în clasa de bază fără a schimba semnătura metodei. Aceasta este în esență o înlocuire de metodă și este noua metodă definită în subclasă care este apelată atunci când programul rulează. De obicei, când se suprascrie o metodă, se folosește adnotarea @Override, care îi spune compilatorului să verifice semnăturile metodelor suprascrise și suprascrise. În cele din urmă Pentru a vă stila programul în concordanță cu conceptul OOP și cu principiile OOP ale java, urmați aceste sfaturi:
  • evidențiați principalele caracteristici ale obiectului;
  • evidențiază proprietățile și comportamentul comune și folosește moștenirea la crearea obiectelor;
  • folosiți tipuri abstracte pentru a descrie obiecte;
  • încercați să ascundeți întotdeauna metodele și câmpurile legate de implementarea internă a clasei.

(după cum înseamnă OOP) este, în primul rând, o paradigmă de programare.
Paradigma de programare definește modul în care programatorul vede execuția programului.
Deci, pentru paradigma OOP, este caracteristic ca programatorul să considere programul ca un set de obiecte care interacționează, în timp ce, de exemplu, în programarea funcțională, programul este reprezentat ca o secvență de calcul al funcțiilor. Programarea procedurală, sau, cum se mai spune corect, operațională clasică, presupune scrierea unui algoritm pentru a rezolva o problemă; cu toate acestea, proprietățile așteptate ale rezultatului final nu sunt descrise sau indicate. Programarea structurată aderă practic la aceleași principii ca și programarea procedurală, completându-le doar puțin cu trucuri utile.
Paradigmele de programare non-procedurală, care includ paradigma orientată pe obiecte, au idei complet diferite.
Definiția lui Gradi Buch este: „ Programare orientată pe obiecte Este o metodologie de programare care se bazează pe reprezentarea unui program ca un set de obiecte, fiecare dintre acestea fiind o implementare a unei anumite clase (tip de tip special), iar clasele formează o ierarhie bazată pe principiile moștenirii”.
Programarea structurală și orientată pe obiecte se bazează pe o astfel de metodă științifică ca descompunere- o metodă care utilizează structura problemei și vă permite să împărțiți soluția unei probleme mari comune în rezolvarea unei secvențe de probleme mai mici. Descompunerea OOP apare nu în funcție de algoritmi, ci în funcție de obiectele folosite pentru a rezolva problema. Această descompunere reduce dimensiunea sistemelor software prin reutilizarea mecanismelor comune. Se știe că sistemele de programare vizuală sau sistemele bazate pe principiile programării orientate pe obiecte sunt mai flexibile și mai ușor de evoluat în timp.

Istoria dezvoltării POO datează de la sfârșitul anilor 60. Primul limbaj orientat pe obiecte a fost limbajul de programare Simula, creat la un centru de calculatoare din Norvegia. Limbajul a fost destinat să simuleze situații din lumea reală. O caracteristică a Simula a fost că un program scris într-un limbaj era organizat prin programarea obiectelor. Obiectele aveau instrucțiuni numite metode și date numite variabile; metodele și datele au determinat comportamentul obiectului. În timpul procesului de modelare, obiectul s-a comportat conform comportamentului său standard și, dacă este necesar, a schimbat datele pentru a reflecta influența acțiunii atribuite.

Sunt destui limbaje de programare orientate pe obiecte, dintre care cele mai populare sunt în prezent C ++, Delphi, Java, Visual Basic, Flash. Dar, în plus, multe limbaje care sunt de obicei considerate o paradigmă procedurală au și proprietăți OOP, putând lucra cu obiecte. Deci, programarea orientată pe obiecte în C este o secțiune mare de programare în acest limbaj, același lucru este valabil și pentru OOP în python și multe alte limbaje structurate.

Când vorbim despre POO, apare adesea o altă definiție - programare vizuală... În plus, oferă o utilizare extinsă a prototipurilor de obiecte, care sunt definite ca clase de obiecte.
Evenimente.În multe medii de programare vizuală, este implementată o caracteristică (pe lângă încapsulare, polimorfism și moștenire) a unui obiect - un eveniment. Evenimentele din programarea orientată pe obiecte reprezintă capacitatea de a procesa așa-numitele mesaje (sau evenimente) primite de la sistemul de operare Windows sau de la programul însuși. Acest principiu este tipic pentru toate componentele mediului care procesează diverse evenimente care au loc în timpul execuției programului. De fapt, un eveniment este un fel de acțiune care activează reacția standard a unui obiect. Un eveniment poate fi luat în considerare, de exemplu, a face clic pe un buton al mouse-ului, a trece cursorul mouse-ului peste un element de meniu, a deschide o filă etc. Ordinea de executare a anumitor acțiuni este determinată tocmai de evenimentele care au loc în sistem și de reacția obiectelor la acestea.
Clase și obiecte în POO- concepte diferite. Conceptul de clasă în POO este un tip de date (la fel ca, de exemplu, Real sau String), iar un obiect este o instanță specifică a unei clase (copia sa), stocată în memoria computerului ca o variabilă de tipul corespunzător. .
Clasă este un tip de date structurate. Clasa include o descriere a câmpurilor de date, precum și a procedurilor și funcțiilor care lucrează cu aceste câmpuri de date. Metoda POO- acestea sunt astfel de proceduri și funcții în raport cu clasele.
Clasele au câmpuri (cum ar fi tipul de date de înregistrare), proprietăți care sunt similare câmpurilor, dar au descriptori suplimentari care definesc mecanisme de scriere și citire a datelor și metodelor - subrutine care au ca scop modificarea câmpurilor și proprietăților unei clase.

Principiile de bază ale POO

Pe lângă gestionarea evenimentelor, principiile programării orientate pe obiecte sunt încapsularea, moștenirea, subclasarea și polimorfismul. Ele sunt deosebit de utile și esențiale atunci când se dezvoltă aplicații replicabile și ușor de întreținut.
Un obiect combină metode și proprietăți care nu pot exista separat de el. Prin urmare, dacă obiectul este șters, atunci proprietățile sale și metodele asociate sunt șterse. La copiere, se întâmplă același lucru: obiectul este copiat ca întreg. încapsulare OOP- aceasta este caracteristica descrisă.

Principiul și subclasele de moștenire OOP

Absolut toate obiectele sunt create pe baza claselor, în timp ce ele moștenesc proprietățile și metodele acestor clase. La rândul lor, clasele pot fi create pe baza altor clase (părinți), apoi astfel de clase se numesc subclase (descendenți). Subclasele moștenesc toate proprietățile și metodele clasei părinte. În plus, pentru o subclasă sau o clasă descendentă, puteți defini proprietăți și metode noi, proprii, precum și modificați metodele clasei părinte. Modificările aduse proprietăților și metodelor unei clase părinte sunt urmărite în subclase bazate pe această clasă, precum și în obiectele create din subclase. Aceasta este moștenirea OOP.

Polimorfismul OOP

În programarea orientată pe obiecte, polimorfismul este caracterizat ca interschimbabilitatea obiectelor cu aceeași interfață. Acest lucru poate fi explicat astfel: clasa descendentă moștenește instanțele metodelor clasei părinte, dar execuția acestor metode se poate produce într-un mod diferit, corespunzător specificului clasei descendente, adică modificată.
Adică, dacă în programarea procedurală numele unei proceduri sau funcție identifică în mod unic codul executabil asociat cu o anumită procedură sau funcție, atunci în programarea orientată pe obiecte puteți folosi aceleași nume de metodă pentru a efectua diferite acțiuni. Adică rezultatul executării aceleiași metode depinde de tipul de obiect căruia i se aplică această metodă.

Site-ul prezintă o teorie parțială a programării orientate pe obiecte pentru începători și exemple OOP de rezolvare a problemelor. Lecțiile de site OOP sunt algoritmi detaliați pentru sarcină. Pe baza efectuării acestor lucrări de laborator, studentul va fi capabil să rezolve în mod independent alte probleme similare în viitor.
Vă dorim o învățare ușoară și distractivă despre programarea orientată pe obiecte!

Top articole similare