Postulatele OOP. Probleme de programare structurată. Crearea de noi tipuri de date

O idee generală despre programarea orientată pe obiecte și caracteristicile sale a fost discutată în prima lecție. Aici rezumăm materialul studiat în acest curs.

În Python, toate obiectele sunt derivate din clase și moștenesc atribute de la acestea. În acest caz, fiecare obiect își formează propriul spațiu de nume. Python le suportă caracteristici cheie programare orientată pe obiecte cum ar fi moștenirea, încapsularea și polimorfismul. Totuși, încapsularea în sensul de ascundere Date Python suportă numai în cadrul convenției, nu și sintaxa limbajului.

Cursul nu a acordat atenție moștenirii multiple, când o clasă copil moștenește de la mai multe clase părinte. Această moștenire este pe deplin acceptată în Python și face posibilă combinarea atributelor a două sau mai multe clase într-o clasă derivată. În cazul moștenirii multiple, anumite caracteristici ale căutării de atribute trebuie luate în considerare.

Polimorfismul permite obiectele diferite clase au interfețe similare. Se implementează prin declararea metodelor în ele cu aceleasi nume. Metodele de supraîncărcare a operatorului pot fi, de asemenea, considerate o manifestare a polimorfismului ca o caracteristică a OOP.

Pe lângă moștenire, încapsulare și polimorfism, există și alte caracteristici ale OOP. Aceasta este compoziția sau agregarea, atunci când o clasă include apeluri către alte clase. Ca urmare, la crearea unui obiect dintr-o clasă agregată, sunt create obiecte din alte clase, care sunt componente primul.

Clasele sunt de obicei plasate în module. Fiecare modul poate conține mai multe clase. La rândul lor, modulele pot fi combinate în pachete. Python folosește pachete pentru a organiza spațiile de nume.

Avantajele OOP

Caracteristicile programării orientate pe obiecte îi oferă o serie de avantaje.

Deci OOP vă permite să utilizați același cod de program cu date diferite. Pe baza claselor, sunt create multe obiecte, fiecare dintre ele poate avea valori proprii câmpuri. Nu este nevoie să introduceți multe variabile, deoarece obiectele au propriile lor spații de nume individuale. În acest sens, obiectele sunt similare structurilor de date. Un obiect poate fi gândit ca un fel de pachet de date la care sunt atașate instrumente pentru procesarea acestuia – metode.

Moștenirea vă permite să evitați scrisul cod nou, dar utilizați și personalizați unul existent prin adăugarea și redefinirea atributelor.

Dezavantajele OOP

OOP vă permite să reduceți timpul de scriere a codului sursă, dar își asumă un rol mai mare analiză preliminară domeniul subiectuluiși design. Mult mai mult depinde de corectitudinea deciziilor în această etapă decât de scrierea efectivă a codului sursă.

Trebuie înțeles că aceeași problemă poate fi rezolvată prin diferite modele de obiecte, fiecare dintre ele va avea propriile sale avantaje și dezavantaje. Doar un dezvoltator cu experiență poate spune care dintre ele va fi mai ușor de extins și de întreținut în viitor.

Caracteristicile OOP în Python

În comparație cu multe alte limbaje, programarea orientată pe obiecte în Python are o serie de caracteristici speciale.

Totul este un obiect - un număr, un șir, o listă, o funcție, o instanță a unei clase, clasa în sine, un modul. Deci o clasă este un obiect capabil să genereze alte obiecte - instanțe.

Nu există tipuri simple de date în Python. Toate tipurile sunt clase.

Nu se pune accent pe încapsulare în Python. atenție deosebită. În alte limbaje de programare, de obicei nu puteți accesa direct o proprietate declarată într-o clasă. Poate fi furnizată o metodă specială pentru a-l schimba. În Python, nu este considerat reprobabil să accesezi direct proprietăți.

Și în sfârșit

Python este, la urma urmei, un limbaj de scripting interpretat. Deși scriu pe el, printre altele: proiecte majore, este adesea folosit în dezvoltarea web, administrarea sistemului pentru crearea de mici programe script. În acest caz, instrumentele de limbaj încorporate sunt de obicei suficiente, nu este nevoie să vă „inventați” propriile clase.

Toate limbajele orientate pe obiecte folosesc trei principii de bază ale programării orientate pe obiecte:

  • Încapsulare. Cum limba dată se ascunde caracteristici interne implementarea obiectului?
  • Moştenire. Cum permite această limbă reutilizarea codului?
  • Polimorfismul. Cum permite un anumit limbaj ca obiectele înrudite să fie interpretate într-un mod unificat?

Primul principiu al OOP este încapsularea. Încapsulare este un mecanism de programare care leagă împreună codul și datele pe care le manipulează, protejându-le de acestea acces externși aplicarea incorectă și ascunderea detaliilor de implementare prin intermediul limbajului. De exemplu, să presupunem că folosim o clasă care reprezintă obiecte precum Pen. O astfel de clasă încapsulează capacitatea intrinsecă a obiectelor de a desena puncte, linii și forme de grosimi și culori diferite. Principiul încapsulării simplifică sarcina de programare în sensul că nu mai este nevoie să vă faceți griji cu privire la numeroasele linii de cod care efectuează activitatea clasei de stilouri din culise. Tot ceea ce este necesar este să creați o instanță a clasei de stilouri și să interacționați cu ea apelând funcțiile acesteia.

Unul dintre aspecte importanteÎncapsularea este protecția datelor. În mod ideal, datele care caracterizează starea unui obiect ar trebui definite ca fiind închise și inaccesibile mediului extern. În acest caz, mediul extern al obiectului va fi obligat să solicite dreptul de modificare sau citire a valorilor corespunzătoare. Astfel, conceptul de încapsulare reflectă regula generala, conform căruia câmpurile de date ale unui obiect nu ar trebui să fie direct accesibile din interfață deschisă. Dacă utilizatorul trebuie să schimbe starea unui obiect, atunci trebuie să facă acest lucru nu direct, ci indirect, folosind funcțiile de citire ( obţine()) și modificări ( set()). În C#, accesibilitatea datelor este implementată la nivel de sintaxă folosind cuvinte cheie public, privat, protejat, Și protejat intern.

În cadrul unui obiect, codul, datele sau atât codul, cât și datele pot fi închise la alte obiecte sau deschise. Codul și datele proprietare sunt cunoscute și accesibile doar din altă parte a acelui obiect (adică numai obiectului însuși). Prin urmare să cod închis iar datele nu pot fi accesate dintr-o parte a programului care există în afara obiectului.

Următorul principiu OOP este moştenire , adică capacitatea limbajului de a oferi construcția de definiții de clase noi pe baza definițiilor claselor existente. În esență, moștenirea vă permite să extindeți comportamentul clasei de bază (numită și clasa de părinți) prin construirea unei subclase (numite derivat sau clasa de copii), moștenind caracteristici și funcţionalitate clasa de părinți. În esență, această formă de moștenire este reutilizare codul de program al unei clase (de bază) în alte clase (derivate din aceasta). De obicei, în acest caz, clasa copil extinde capacitățile clasei de bază prin adăugarea unor capacități noi care nu sunt prezente în clasa de bază. clasa de bază. Această formă de moștenire se numește „este-a” (adică să fie la fel, dar cu capacități mai mari).


O altă formă de reutilizare a codului este modelul de localizare/delegare (cunoscut și ca relația de localizare „are-a”). Acest formular nu este folosit pentru a crea relații clasă-subclasă. În schimb, o clasă poate defini o variabilă a unei alte clase în interiorul ei și poate expune o parte sau toată funcționalitatea ei lumii exterioare. În acest caz, clasa este mai mult ca un container care conține instanțe ale altor clase.

Al treilea principiu al OOP este polimorfism . Caracterizează capacitatea unui limbaj de a interpreta obiectele înrudite în același mod. Această caracteristică a unui limbaj orientat pe obiecte permite unei clase de bază să definească un set de membri pentru toate clasele derivate. În mod formal, acest membru comun este numit interfață polimorfă. O interfață polimorfă pentru o clasă este construită prin definirea unui număr arbitrar virtualŞi abstract funcții. Funcție virtuală clasă Can schimbare într-o clasă derivată și o funcție abstractă poate fi numai suprascrie. Când clasele derivate suprascriu funcțiile definite într-o clasă de bază, ele suprascriu în esență modul în care răspund la o solicitare corespunzătoare. Pe lângă capacitatea de a suprascrie funcții, limbajul C# oferă posibilitatea de a utiliza o altă formă de polimorfism - supraîncărcarea funcției. Supraîncărcarea ar trebui considerată ca oportunitate suplimentară distinge funcţiile de acelasi nume, diferit sume diferite sau tipul de argumente. Prin urmare, supraîncărcarea poate fi aplicată nu numai funcțiilor membre ale clasei, ci și funcțiilor globale.

Probabil jumătate din posturile vacante (dacă nu mai multe) necesită cunoștințe și înțelegere a OOP. Da, această metodologie a captivat cu siguranță mulți programatori! De obicei, înțelegerea OOP vine cu experiență, deoarece practic nu există materiale adecvate și accesibile pe acest subiect. Și chiar dacă există, este departe de a fi sigur că cititorii se vor împiedica de ele. Sper că voi putea explica principiile acestei metodologii minunate, după cum se spune, pe degetele mele.

Deci, deja la începutul articolului am menționat deja termenul „metodologie”. Când se aplică programării, acest termen implică prezența oricărui set de modalități de organizare a codului, metode de scriere a acestuia, aderând la care, programatorul va putea scrie programe complet utilizabile.

OOP (sau programarea orientată pe obiecte) este o modalitate de organizare a codului programului în care principalele elemente de bază ale programului sunt obiecte și clase, iar logica programului este construită pe interacțiunea lor.


Despre obiecte și clase

Clasă- aceasta este o structură de date care poate fi creată chiar de programator. În termeni OOP, o clasă constă din câmpuri(în termeni simpli – variabile) și metode(în termeni simpli – funcții). Și, după cum s-a dovedit, combinația de date și funcții pentru a lucra asupra ei într-o singură structură oferă o putere de neimaginat. Obiect este o instanță specifică a unei clase. Urmând analogia unei clase cu o structură de date, un obiect este o structură de date specifică care are anumite valori atribuite câmpurilor sale. Să explic cu un exemplu:

Să presupunem că trebuie să scriem un program care calculează perimetrul și aria unui triunghi, care este dat de două laturi și unghiul dintre ele. Pentru a scrie un astfel de program folosind OOP, va trebui să creăm o clasă (adică o structură) Triunghi.

Clasa Triunghi va stoca trei câmpuri (trei variabile): latura A, latura B, unghiul dintre ele; și două metode (două funcții): calculați perimetrul, calculați aria. Cu această clasă putem descrie orice triunghi și putem calcula perimetrul și aria.

/** * Clasa triunghi.

*/ clasa Triunghi ( /** * Metodă specială numită constructor de clasă. * Preia trei parametri ca intrare: * lungimea laturii A, lungimea laturii B, * unghiul dintre aceste laturi (în grade) */ Triunghi (latură dublă A, dublu sideB , dublu unghiAB) ( this.sideA = sideA; this.sideB = sideB; this.angleAB = angleAB; ) dublu sideA //Câmpul de clasă, stochează valoarea laturii A din triunghiul dublu B descris; triunghi dublu angleAB //Câmpul de clasă stochează unghiul (în grade) dintre două laturi în triunghiul descris /** * Metoda clasei care calculează aria triunghiului */ double getSquare() ( dublu pătrat = this.sideA; * this .sideB * Math.sin(this.angleAB * Math.PI / 180 * Metoda clasei care calculează perimetrul unui triunghi */ double getPerimeter() ( double sideC = Math.sqrt(Math.pow); ) (this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos(this.angleAB * Math.PI / 180));

dublu perimetru = this.sideA + this.sideB + sideC;

perimetrul de întoarcere; ) ) Dacă adăugăm următorul cod în interiorul clasei:

/** * Aici rulează programul */ public static void main(String args) ( //Valorile 5, 17, 35 merg în constructorul clasei Triunghi Triunghi triunghi1 = triunghi nou (5, 17, 35) ); System.out .println("Aria triunghiului1: "+triangle1.getSquare()); System.out.println("Perimetrul triunghiului1: "+triangle1.getPerimeter()); , 8, 60 mergeți la constructorul clasei Triangle triangle2 = new Triangle(6, 8, 60 System.out.println("Area triunghiului1: "+triangle2.getSquare()); „Perimetrul triunghiului1: „+triunghi2.getPerimeter());

atunci programul poate fi deja lansat pentru execuție. Aceasta este o caracteristică

limbajul java

. Dacă clasa are o astfel de metodă

Public static void main (Argumente șir)

atunci această clasă poate fi executată. Să ne uităm la codul mai detaliat. Să începem cu linia

Același lucru se întâmplă și pentru a doua instanță a clasei Triunghi.

Înțelegerea esenței claselor și construcția obiectelor concrete este un prim pas sigur pentru înțelegerea metodologiei OOP.

Încă o dată, cel mai important lucru:

OOP- aceasta este o modalitate de organizare a codului programului;

Clasă- aceasta este o structură de date personalizată care reunește date și funcții pentru lucrul cu acestea (câmpuri de clasă și metode de clasă);

Obiect este o instanță specifică a unei clase ale cărei câmpuri primesc valori specifice.


Trei cuvinte magice

OOP include trei abordări cheie: moștenire, încapsulare și polimorfism. Pentru început, voi da definiții de pe wikipedia:

Încapsularea este o proprietate a sistemului care vă permite să combinați datele și metodele care funcționează cu acestea într-o clasă. Unele limbaje (de exemplu, C++) echivalează încapsularea cu ascunderea, dar cele mai multe (Smalltalk, Eiffel, OCaml) fac distincție între aceste concepte.

Moștenirea este o proprietate a sistemului care vă permite să descrieți noua clasa bazat pe unul existent cu funcționalitate împrumută parțial sau complet. Clasa din care este derivată moștenirea se numește bază, părinte sau superclasă.

O nouă clasă este un descendent, moștenitor, copil sau clasă derivată.

Polimorfismul 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.
Înțelegerea a ceea ce înseamnă de fapt toate aceste definiții este destul de dificilă. În cărțile de specialitate care acoperă acest subiect, fiecare definiție dedică adesea un întreg capitol, dar măcar un paragraf. Deși, esența a ceea ce un programator trebuie să înțeleagă și să-și imprime pentru totdeauna în creierul său este foarte puțină.


Și ca exemplu pentru analiză, vom folosi cifrele pe un avion. Din geometria școlii știm că pentru toate figurile descrise pe un plan, este posibil să se calculeze perimetrul și aria. De exemplu, pentru un punct ambii parametri sunt egali cu zero. Pentru un segment, putem calcula doar perimetrul. Și pentru un pătrat, dreptunghi sau triunghi - ambele. Acum vom descrie această sarcină în termeni OOP. De asemenea, este o idee bună să înțelegeți lanțul de raționament care are ca rezultat o ierarhie de clasă, care, la rândul său, este întruchipată în codul de lucru. Să mergem: Deci, punctul este cel mai mic figură geometrică

/** * Clasa de puncte. Clasa de bază */ clasa Point ( /** * Constructor gol */ Point() () /** * Metoda clasei care calculează aria unei figuri */ double getSquare() ( return 0; ) /** * Metoda clasei care calculează perimetrul figurii */ double getPerimeter() ( return 0; ) /** * Metoda clasei care returnează o descriere a figurii */ String getDescription() ( return "Point"; ) )

Clasa Point rezultată are un constructor gol deoarece în acest exemplu Lucrăm fără coordonate specifice, dar funcționăm doar cu parametri și valori laterale. Deoarece punctul nu are laturi, nu este nevoie să-i treci niciun parametru. De asemenea, rețineți că clasa are metodele Point::getSquare() și Point::getPerimeter() pentru calcularea ariei și a perimetrului, ambele returnează 0. Pentru un punct, acest lucru este logic.


Deoarece punctul nostru este baza tuturor celorlalte figuri, moștenim clasele acestor alte figuri din clasa Point. Să descriem clasa unui segment moștenit din clasa unui punct:

/** * Class Line Segment */ class LineSegment extinde Point ( LineSegment(double segmentLength) ( this.segmentLength = segmentLength; ) double segmentLength; // Lungimea liniei /** * Metoda clasa suprascrisă care calculează aria lui linia */ double getSquare() ( return 0; ) /** * Metoda de clasa suprascrisă care calculează perimetrul unui segment */ double getPerimeter() ( return this.segmentLength; ) String getDescription() ( return "Lungimea segmentului: " + this.segmentLength; ))

Clasa LineSegment extinde Point

înseamnă că clasa LineSegment moștenește din clasa Point. Metodele LineSegment::getSquare() și LineSegment::getPerimeter() suprascriu metodele clasei de bază corespunzătoare. Aria unui segment este întotdeauna zero, iar aria perimetrului este egală cu lungimea acestui segment.

Acum, ca și clasa segmentului, vom descrie clasa triunghiului (care moștenește și din clasa de puncte):

/** * Clasa triunghi.

*/ clasa Triunghiul extinde Punctul ( /** * Constructorul clasei. Preia trei parametri ca intrare: * lungimea laturii A, lungimea laturii B, * unghiul dintre aceste laturi (în grade) */ Triunghi (latura dublaA, latura dublaB, unghi dubluAB ) ( this.sideA = sideA; this.sideB = sideB; this.angleAB = angleAB; ) dublu sideA //Câmpul de clasă, stochează valoarea laturii A în triunghiul dublu sideB //Câmpul de clasă; valoarea laturii B în triunghiul dublu unghi AB //Câmpul de clasă stochează unghiul (în grade) dintre două laturi din triunghiul descris /** * Metoda clasei care calculează aria triunghiului */ dublu; getSquare() ( dublu pătrat = (this.sideA * this.sideB * Math.sin(this.angleAB * Math.PI / 180)))/2; return square; /** * Metoda de clasă care calculează perimetrul unui triunghi */ double getPerimeter() ( dublu laturăC = Math.sqrt(Math. pow(this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos( aceasta.unghiAB * Math.PI / 180));
dublu perimetru = this.sideA + this.sideB + sideC;

perimetrul de întoarcere;

Am creat o matrice de obiecte din clasa Point și, deoarece clasele LineSegment și Triangle moștenesc din clasa Point, le putem plasa în această matrice. Se dovedește că putem considera fiecare figură care se află în tabloul figures ca un obiect al clasei Point.


Iată ce este polimorfismul: nu se știe cărei clase îi aparțin obiectele din tabloul de figuri, dar deoarece toate obiectele din această matrice aparțin aceleiași clase de bază Point, atunci toate metodele care sunt aplicabile clasei Point sunt de asemenea aplicabile la clasele sale descendente.


Acum despre încapsulare. Faptul că am plasat parametrii unei figuri și metodele de calcul a ariei și a perimetrului într-o singură clasă este încapsularea figurilor în clase separate. Faptul că folosim o metodă specială în clasă pentru a calcula perimetrul este încapsularea, am încapsulat calculul perimetrului în metoda getPerimiter(). Cu alte cuvinte, încapsularea ascunde implementarea (poate cea mai scurtă și, în același timp, mai mare definiție a încapsulării).

Exemplu complet de cod:

Import java.util.ArrayList; class Main ( /** * Aici rulează programul */ public static void main(String args) ( //ArrayList este o structură specială de date în java // care vă permite să stocați obiecte de un anumit tip într-o matrice. ArrayList figures = new ArrayList ();

Informații generale

OOP este un stil de programare care a apărut în anii 80 ai secolului XX. Spre deosebire de limbajele procedurale, unde datele și instrucțiunile de prelucrare a acestora există separat, în programarea orientată pe obiecte aceste informații sunt combinate într-o singură entitate.

Moştenire

Principiile de bază ale POO

Polimorfismul

Al doilea principiu al OOP, moștenirea, este capacitatea unei clase de a folosi metodele alteia fără a repeta implementarea lor efectivă. Moștenirea vă permite să scăpați de redundanța în codul sursă.

Un alt principiu al OOP este polimorfismul. Utilizarea acestuia înseamnă că, pentru manipularea obiectelor de diferite grade de complexitate, puteți crea o interfață care va reacționa diferit la evenimente și, în același timp, va implementa corect sarcinile atribuite.

Principiile OOP sunt utilizate în cele mai populare limbaje de programare, cum ar fi C++ și Java, în care sunt dezvoltate o parte semnificativă a programelor și aplicațiilor. Există, de asemenea, limbaje OOP mai puțin utilizate - Delphi, Object Pascal, Ruby și multe altele.

Critica la OOP

În ciuda declarațiilor în mare parte pozitive față de această metodologie, principiile POO sunt adesea criticate. La fel ca OOP, are dezavantajele sale.

În primul rând, dificultatea tranziției. Va dura destul de mult timp pentru a înțelege principiile OOP, în special pentru persoanele care lucrează îndeaproape numai cu limbaje de programare procedurală.

În al doilea rând, dezavantajul este o documentare mai complexă, deoarece va fi necesar nu numai să se descrie clase și obiecte, ci și cazuri specifice de implementare a acestora.

În al treilea rând, universalitatea excesivă a metodelor poate duce la faptul că cod sursă iar programele dezvoltate vor fi supraîncărcate cu funcții și capabilități care nu sunt solicitate în acest caz particular. În plus, ei notează ineficiență în ceea ce privește alocarea memoriei. Cu toate acestea, indiferent de opiniile altora, numărul de programatori OOP este în continuă creștere, iar limbajele înseși se dezvoltă rapid.

Java este un limbaj orientat pe obiecte. 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ă înțelegem 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 elemente sunt obiecte și fiecare dintre ele are un set de caracteristici specifice, comportament și scop. Suntem obișnuiți cu obiectele și le folosim întotdeauna în scopuri foarte specifice. De exemplu, dacă trebuie să ajungem la muncă, folosim o mașină, dacă vrem să mâncăm, folosim vase, iar dacă trebuie să ne relaxăm, avem nevoie de o canapea confortabilă. O persoană este obișnuită să gândească obiectiv pentru a rezolva probleme în viata 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. Imaginează-ți ce ai dezvoltat model nou telefon și vrei să-l repari producție în serie. În calitate de designer de telefoane, știi pentru ce este, cum va funcționa și din ce părți va consta (carcasă, microfon, difuzor, fire, butoane etc.). Cu toate acestea, numai tu știi cum să conectezi aceste părți. Cu toate acestea, nu intenționați să produceți telefoane personal pentru aceasta aveți un întreg personal de angajați. Pentru a nu trebui să explicați de fiecare dată cum să conectați părțile telefonului și pentru ca toate telefoanele aflate în producție să iasă la fel, înainte de a începe să le produceți, va trebui să faceți un desen sub formă de descrierea structurii telefonului.În POO, 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 format 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 la modul în care putem trece de la un obiect din lumea reală la un obiect dintr-un program, folosind telefonul ca exemplu. Istoria acestui mijloc de comunicare depăşeşte 100 de ani şi telefon modern , spre deosebire de predecesorul său din secolul al XIX-lea, este un dispozitiv mult mai complex. Când folosim un telefon, nu ne gândim la structura lui și la procesele care au loc în interiorul acestuia. Pur și simplu folosim funcțiile oferite de dezvoltatorii de telefoane - butoane sau touch screen pentru a selecta un număr și a efectua apeluri. Una dintre primele interfețe telefonice a fost un buton pe care îl rotiți pentru a efectua un apel. Desigur, acest lucru nu a fost foarte convenabil. Cu toate acestea, mânerul și-a îndeplinit funcția în mod corespunzător. Dacă te uiți la cel mai modern și chiar primul telefon, poți identifica 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. În esență, acesta este ceea ce face un telefon un telefon și nu altceva. Acum am aplicat principiul în POO - evidențiind cel mai mult caracteristici importante și informații despre obiect. Acest principiu se numește abstractizare. Abstracția în POO poate fi definită și ca o modalitate de reprezentare a elementelor unei probleme 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 lucru este separarea de la nesemnificativ în contextul problemei care se rezolvă. În acest caz, pot exista mai multe niveluri de abstractizare. Să încercăm să aplicăm principiul abstracției pe telefoanele noastre. În primul rând, 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, folosind abstractizarea, putem evidenția obiectele din această ierarhie. Informații generale: un tip general de obiect abstract - telefon, caracteristici generale telefon - anul creării sale și interfață comună- Toate telefoanele sunt capabile să primească și să trimită apeluri. Iată cum 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 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 utilizarea 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 desemnăm această individualitate? Cum ne putem asigura că niciunul dintre utilizatori nu ne poate sparge accidental sau intenționat telefonul sau nu poate încerca să transforme un model în altul? Pentru lumea obiectelor reale, răspunsul este evident: trebuie să puneți toate piesele în corpul telefonului. La urma urmei, dacă nu facem acest lucru și lăsăm toate interiorul telefonului și firele care le conectează afară, cu siguranță va exista un experimentator curios care va dori să „îmbunătățească” funcționarea telefonului nostru. Pentru a exclude o astfel de interferență în proiectarea și funcționarea unui obiect, OOP utilizează principiul încapsulării - un alt principiu de bază 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ță publică pentru lucrul cu obiectul. Sarcina programatorului este de a determina pentru ce atribute și metode vor fi disponibile acces deschis, și care sunt implementarea internă a obiectului și nu ar trebui să fie accesibile pentru modificări.

Încapsulare și control acces

Să presupunem că în timpul producției, informațiile despre acesta sunt gravate pe spatele telefonului: anul fabricației sau sigla companiei producătorului. Această informație caracterizează destul de specific acest model- starea lui. Putem spune că dezvoltatorul telefonului s-a ocupat de imuabilitatea acestor informații - este puțin probabil ca cineva să se gândească la eliminarea gravurii. În lumea Java, starea obiectelor viitoare este descrisă într-o clasă folosind câmpuri, iar comportamentul lor este descris folosind metode. Capacitatea de a schimba starea și comportamentul se realizează folosind modificatori de acces la câmpuri și metode - privat, protejat, public , și de asemenea 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. privat Folosind cod, clasa poate fi descrisă după cum urmează: clasă publică SomePhone ( private int year; private String company; public SomePhone (int year, String company) ( acest . year = an; this . company = company; ) private void openConnection ( ) ( / /findComutator //openNewConnection... ) public void call () ( openConnection () ; System. out. println ("Apelarea numărului") ; ) public void ring () ( System. out. println ("Ding). -ding") ; ) ) Modificator privat face câmpurile și metodele unei clase disponibile numai în cadrul acelei clase. Aceasta înseamnă că puteți accesa privat câmpuri din exterior este imposibil, așa cum nu există nicio modalitate de a suna public . 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 perturba activitatea. Pentru a lucra cu obiectul nostru, lăsăm deschise metodele de apel și apel folosind un modificator

Furnizarea de metode publice de lucru cu un obiect este, de asemenea, parte a mecanismului de încapsulare, deoarece dacă accesul la un obiect este complet refuzat, acesta va deveni inutil.

Moştenire Să ne uităm din nou la diagrama telefonică. Se poate observa ca reprezinta o ierarhie in care modelul aflat mai jos are toate caracteristicile modelelor situate mai sus pe ramura, plus propriile sale. De exemplu, un smartphone folosește retea celulara pentru comunicare (are proprietățile unui telefon mobil), este wireless și portabil (are proprietăți telefon fără fir ) și poate primi și efectua apeluri (folosind proprietățile telefonului). În acest caz, putem vorbi despre moștenirea proprietăților obiectului. Să ne uităm la un exemplu de creare a unei clase de smartphone folosind moștenirea. Toate telefoanele fără fir funcționează de la baterii, care au o anumită durată de viață î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, de asemenea, o implementare a metodelor de apel și apel la această clasă: public class CellPhone extinde WirelessPhone ( public CellPhone (int year, int hour) ( super (an, hour); ) @Override public void call ( int outputNumber) ( System. out. println ("Numărul 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 acceptate de acest dispozitiv pe smartphone. sistem de operare

, 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 ("Instalez " + program + "for" + operationSystem) ; ) ) După cum puteți vedea, am creat foarte puțin cod nou pentru a descrie clasa Smartphone, dar am primit un nou clasa cu noua functionalitate. Utilizarea acestui principiu de OOP java poate reduce semnificativ cantitatea de cod și, prin urmare, ușurează munca programatorului.

Polimorfismul Dacă ne uităm la toate modelele de telefoane, atunci, în ciuda diferențelor de aspect și design ale modelelor, putem identifica un anumit– 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 OOP, care ne este deja cunoscut, abstractizarea în termeni de programare, putem spune că obiectul telefon are o singură 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 detaliile tehnice ale dispozitivului. Deci, folosești în mod constant telefon mobil, și puteți efectua cu ușurință un apel de la omologul său fix. Un principiu în POO când un program poate folosi obiecte cu aceeași interfață fără informații despre structura internă obiectul este numit 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 șir privat; utilizator public (nume șir) ( acest nume = nume; ) public void callAnotherUser (număr int, telefon AbstractPhone) ( // acesta este polimorfism - folosind telefonul de tip abstract AbstractPhone în cod! telefon. apel (număr) ; ) ) ) Acum să descriem diverse modele telefoane. Unul dintre primele modele de telefon: public class ThomasEdisonPhone extinde AbstractPhone ( public ThomasEdisonPhone (int year) ( super (an) ; ) @Override public void call (int outputNumber) ( System. out. println ("Rotiți butonul") ; System ; .println(„Vă rugăm să furnizați numărul dumneavoastră de telefon, domnule” );) @Override public void ring (int inputNumber) ( System. out. println ( „Telefonul sună” ) ; ) ) Normal telefon fix: public class Phone extinde AbstractPhone ( public Phone (înt year) ( super (an) ; ) @Override public void call (int outputNumber) ( System. out. println ("Număr apelant" + outputNumber) ; ) @Override public void ring (int inputNumber) ( System. out. println ( „Telefonul sună” ) ; ) ) ) Și, în sfârșit, un telefon video cool: VideoPhone public de clasă extinde AbstractPhone ( VideoPhone public (înt year) ( super (an) ; ) @Override apel public void (int outputNumber) ( System. out. println ( „Conectez un canal video pentru abonat”+ inputNumber) ; ) ) Să creăm obiecte în metoda main() și să testăm metoda callAnotherUser: AbstractPhone firstPhone = new ThomasEdisonPhone (1879) ; AbstractPhone phone = telefon nou (1984); AbstractPhone videoPhone= nou VideoPhone (2018) ; Utilizator utilizator = utilizator nou ("Andrey"); utilizator. callAnotherUser(224466, firstPhone); // Rotiți butonul//Vă rugăm să furnizați numărul abonatului, domnule utilizator. callAnotherUser(224466, telefon);//Numărul de apel 224466 utilizator. callAnotherUser(224466, videoPhone);//Conectarea unui canal 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 tip specific
  • obiectul apelant în timpul execuției programului. Acesta este principalul avantaj al polimorfismului - alegerea implementării în timpul execuției programului. În exemplele de clase de telefon de mai sus, am folosit suprascrierea metodei, o tehnică care modifică implementarea metodei definită în clasa de bază fără a schimba semnătura metodei. În esență, acesta este un înlocuitor pentru metodă și este
  • metoda noua
  • , definit într-o subclasă, este apelat atunci când programul se execută. De obicei, atunci când suprascrieți o metodă, este utilizată adnotarea @Override, care solicită compilatorului să verifice semnăturile metodelor suprascrise și suprascrise.
  • Ca urmare