Polimorfism (informatică)

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

În informatică , termenul polimorfism (din grecescul πολυμορφος compus din termenii πολυ very și μορφή formează deci „a avea mai multe forme”) este folosit în sens generic pentru a se referi la expresii care pot reprezenta valori de diferite tipuri (numite expresii polimorfe ). Într-un limbaj netipat , toate expresiile sunt inerent polimorfe.

Termenul este asociat cu două semnificații specifice:

  • în contextul programării orientate pe obiecte , se referă la faptul că o expresie al cărei tip este descris de o clasă A poate lua valori de orice tip descrise de o subclasă A de clasa B ( polimorfism prin incluziune );
  • în contextul programării generice , se referă la faptul că codul programului poate primi un tip ca parametru, mai degrabă decât să îl cunoască a priori ( polimorfism parametric ).

Polimorfism prin incluziune

De obicei, este legat de relațiile de moștenire dintre clase, ceea ce garantează că aceste obiecte, chiar dacă sunt de tipuri diferite, au aceeași interfață : în limbaje de obiecte tastate, instanțele unei subclase pot fi utilizate în locul instanțelor superclasei ( polimorfism prin incluziune) ).

Metodele sau proprietățile suprascrise permit obiectelor aparținând subclaselor din aceeași clasă să răspundă diferit la aceleași utilizări. De exemplu, să presupunem că aveți o ierarhie în care clasele de Cane și Gatto provin din superclasa Animale . Acesta din urmă definește o metodă de cosaMangia() , ale cărei specificații sunt: Returnează un șir care identifică numele alimentelor tipice ale animalului. Cele două metode cosaMangia() definite în clasele Cane și Gatto anulează pe cel pe care îl moștenesc de la Animale și, respectiv, returnează două rezultate diferite în funcție de tipul real al obiectului pe care este invocată metoda. Prin urmare, comportamentul unui program destul de complex poate fi modificat considerabil în funcție de subclasele care sunt instanțiate în timpul rulării și ale căror instanțe sunt transmise diferitelor părți ale codului.

Metodele care sunt redefinite într-o subclasă se numesc polimorfe , deoarece aceeași metodă se comportă diferit în funcție de tipul de obiect pe care este invocată.

În limbile în care variabilele nu au niciun tip, cum ar fi Ruby , Python și Smalltalk , nu există nici o verificare sintactică a metodelor care pot fi numite ( tastarea rațelor ). Pe de o parte, acest lucru extinde posibilitățile polimorfismului dincolo de relațiile de moștenire: în exemplul de mai sus, clasele de Cane și Gatto nu trebuie să fie subclase de Animale , deoarece clienților le pasă doar că cele trei tipuri expun aceeași metodă cu numele cosaMangia și lista de argumente goale. Pe de altă parte, acest lucru mărește posibilitatea erorilor în timpul rulării, deoarece nu este posibil să forțăm clasele să respecte interfața comună și, prin urmare, o posibilă eroare este identificată nu de compilator (cu consecința refuzului de a compila), ci doar când un anumit client va încerca să utilizeze o metodă sau un atribut care nu există sau este definit într-un mod care nu este conform specificațiilor.

Beneficii

Polimorfismul de incluziune permite programului să utilizeze obiecte care expun aceeași interfață, dar implementări diferite. De fapt, interfața de tip bază definește un contract general pe care diferite subclase îl pot satisface în moduri diferite - dar toate sunt conforme cu specificațiile comune stabilite de tipul de bază. În consecință, partea programului care folosește această interfață - numită client în jargon - tratează toate obiectele care furnizează un set dat de servicii într-un mod omogen, indiferent de implementările lor interne (probabil diferite între ele) definite de clasele lor respective . În virtutea acestei posibilități, este posibil să se utilizeze același cod prin personalizarea sau chiar modificarea radicală a comportamentului său, fără a fi necesar să îl rescrieți, ci pur și simplu oferindu-l ca intrare cu o implementare diferită a tipului de bază sau a tipurilor de bază.

Dacă este bine utilizat, polimorfismul permite o structură a obiectului

  • extensibil , deoarece clientul poate fi indus să invoce noi metode personalizate prin includerea acestora într-o clasă specială;
  • rezistent , deoarece orice nevoi viitoare în program sau în scris codul pot fi implementate prin furnizarea unui client deja scris cu o nouă clasă scrisă ad hoc .

Caz de pornire: cifrele

Să presupunem că doriți să dezvoltați un program capabil să deseneze poligoane de o anumită dimensiune pe ecran. Fiecare poligon trebuie desenat într-un mod diferit, folosind bibliotecile furnizate de limba utilizată.

Întrucât, în timpul rulării, nu vom ști exact câte și ce poligoane va trebui să desenăm, este necesar ca compilatorul să poată urmări pătratul , cercul , pentagonul etc. în același obiect, pentru a recunoaște metodele utilizate . Pentru a face acest lucru declarăm o clasă de bază Figura, de la care toate celelalte vor moșteni proprietățile.

Clasa de bază

Exemplu (limbaj Visual Basic ):

 Figura publică a clasei MustInherit

MustOverride Public Sub tragerea la sorți ()
Perimetrul funcției publice MustOverride () ca dublu
Zona de funcții MustOverride publică () ca dublă

Clasa de sfârșit

Tocmai am declarat o clasă care trebuie moștenită din alte clase și niciodată folosită ca clasă de bază, așa-numita clasă abstractă. Mai mult, metodele trebuie înlocuite de clasele care moștenesc de la acesta. Odată ce acest lucru este făcut, putem implementa toate cifrele dorite.

Unele clase derivate

Următorul exemplu omite implementările unor membri

 Piața Clasei Publice
  Figura Moștenirilor

  Partea privată Ca dublă

  Sub public Nou ( ca latură dublă )
  ...
  Sfârșitul Sub
  Proprietate publică Lato () Ca dublă
  ...
  Proprietate finală

  Subîncărcare publică Sub Draw ()
  'Introduceți aici instrucțiunile pentru a desena un pătrat conform bibliotecilor grafice
  ...
  Sfârșitul Sub
  Public Overrides Function Perimeter () Ca dublu
    Partea de întoarcere * 4
  Funcția de sfârșit
  Public Overrides Function Area () ca dublă
    Întoarcere latură * latură
  Funcția de sfârșit
Clasa de sfârșit

Cercul de clasă publică
  Figura Moștenirilor

  Raza privată Ca dublă

  Sub public nou ( raza ca dublă )
  ...
  Sfârșitul Sub
  Raza proprietății publice () ca dublă
  ...
  Proprietate finală

  Subîncărcare publică Sub Draw ()
  'Introduceți aici instrucțiunile pentru a desena un cerc conform bibliotecilor grafice
  ...
  Sfârșitul Sub
  Public Overrides Function Perimeter () Ca dublu
    Raza de întoarcere * 2 * Matematică . PI
  Funcția de sfârșit
  Public Overrides Function Function Area () ca dublă
    Întoarcere rază * rază * Matematică . PI
  Funcția de sfârșit
Clasa de sfârșit

Și așa mai departe cu celelalte figuri. În acest fel, dacă doriți să lucrați cu o serie de figuri, nu există conflicte de tip, ca în exemplul următor:

 Dim Figura ( 5 ) Ca Figura
...
„Să presupunem că utilizatorul introduce 3 pătrate, un cerc și un hexagon (se presupune clasa de hexagon implementată ca mai sus)
'de exemplu. Figura (2) = New Square (4)
„Această instrucțiune, tocmai pentru că Square moștenește din Figura, nu generează erori de compilare
...
Pentru fiecare fig, după cum se arată în figura
  Fig . Draw ()
  Consolă . WriteLine ( Fig . Perimetru )
Următorul

Executorul, la fiecare figură pe care o întâlnește, va apela la subrutina corespunzătoare din clasa din care face parte. Iată cum se întâmplă acest lucru.

Compilare

Polimorfismul apare cu o acțiune combinată de compilator și linker . Contrar a ceea ce se întâmplă în majoritatea cazurilor, timpul de rulare joacă un rol foarte important în executarea codului polimorf, întrucât nu este posibil să se cunoască, în timpul compilării, clasa căreia îi aparțin obiectele instanțiate. Compilatorul are rolul de a pregăti ceea ce este necesar pentru ca executorul să decidă ce metodă să invoce.

În scopul programării polimorfe, nu este necesar să cunoașteți limbajul de asamblare , cu toate acestea este necesar să aveți câteva noțiuni de bază despre adresare pentru a înțelege următoarele.

Ce se întâmplă la compilare
TMV

În cazul în care clasa de bază este compilat, compilatorul identifică metodele care au fost declarate virtuale (cuvinte cheie MustOverride în Visual Basic , virtual în C ++ și simbolul „#“ în UML de proiectare), și construiește un T capabil de V etodes irtual M, indicând semnăturile funcțiilor care trebuie înlocuite . Prin urmare, aceste funcții rămân „orfani”, adică nu au o adresă pentru punctul de intrare.

Când compilatorul se ocupă de clasele derivate, grupează metodele suprascrise într-un nou TMV, cu structură identică cu cea a clasei de bază, indicând de data aceasta adresele punctului de intrare.

În scopuri teoretice, se poate presupune un tabel de acest tip:

Figura Pătrat Cerc
 _Draw: 0x0000
  _Perimetru: 0x0000
  _Zona: 0x0000
_Draw: 0x3453
  _Perimetru: 0xbc1a
  _Zona: 0x25bf
_Draw: 0x52d0
  _Perimetru: 0x52ab
  _Zona: 0xaa25

Nu contează în ce ordine sunt mapate funcțiile, atâta timp cât sunt în aceeași ordine (la același offset ) în tabel. Notă: TMV-urile nu au identificatori la nivel de asamblare: sunt zone simple de memorie cu o lungime fixă ​​(de obicei 32 sau 64 biți). Identificatorii au fost incluși în exemplu numai în scop ilustrativ.

Ce se întâmplă în timpul rulării
legare dinamică

Am văzut că compilatorul lasă spații goale pentru metodele nemapate. Analizați pas cu pas, ca într-o urmă , tot ce se întâmplă în timpul rulării. Cod de referinta:

 Dim Circle ca figura
Circle = New Circle (3)
Cerc . Draw ()

Să presupunem că ați instanțiat un cerc și doriți să-l desenați. Prima afirmație nu are multe funcționalități: pur și simplu rezervă spațiu de stivă pentru variabila Circle de o lungime egală cu Figura. În a doua instrucțiune, această stivă este de fapt populată cu apelul către constructor. În funcție de limbă, TMV-ul din figură este suprascris cu cel al cercului și valoarea 3 este alocată în zona rezervată pentru raza de tip dublu (de obicei 64 de biți ). În cea de-a treia instrucțiune, executantul consultă TMV-ul Cercului și ia adresa primei funcții mapate. Acest lucru se datorează faptului că nu există identificatori de niciun fel la nivel de asamblare. Odată ce adresa a fost recuperată, programul este gata să sară la punctul de intrare Disegna.

Polimorfism parametric

Un alt mecanism disponibil adesea în limbaje tipizate este polimorfismul parametric : în anumite contexte, este posibil să se definească variabile cu un tip parametrizat, care este apoi specificat în timpul utilizării efective. Exemple de polimorfism parametric sunt șabloanele C ++ și genericele Java.

Elemente conexe

Informatică Portal IT : accesați intrările Wikipedia care se ocupă cu IT