Programarea generică este o paradigmă de programare care constă într-o astfel de descriere a datelor și a algoritmilor care pot fi aplicați diferitelor tipuri de date fără a modifica descrierea în sine. Într-o formă sau alta, este susținut de diferite limbaje de programare . Capacitățile de programare generică au apărut pentru prima dată sub formă de generice (funcții generice) în anii 1970 în limbajele Clu și Ada , apoi ca polimorfism parametric în ML și descendenții săi și apoi în multe limbaje orientate pe obiecte , cum ar fi C++ , Python . 1] , Java , Object Pascal [2] , D , Eiffel , limbaje pentru platforma .NET și altele.
Programarea generică este considerată ca o metodologie de programare bazată pe separarea structurilor de date și a algoritmilor prin utilizarea descrierilor abstracte de cerințe [3] . Declarațiile de cerințe abstracte sunt o extensie a conceptului de tip de date abstracte . În loc să descrie un singur tip în programarea generică, se folosește o descriere a unei familii de tipuri care au o interfață comună și un comportament semantic . Un set de cerințe care descriu o interfață și un comportament semantic se numește concept . Astfel, un algoritm scris într-un stil generalizat poate fi aplicat oricărui tip care îl satisface cu conceptele sale. Această posibilitate se numește polimorfism .
Se spune că un tip modelează un concept (este un model al unui concept) dacă își satisface cerințele. Un concept este o rafinare a altui concept dacă îl completează pe acesta din urmă. Cerințele conceptului conțin următoarele informații: [4]
În C++ , OOP este implementat prin funcții virtuale și moștenire, în timp ce OP (programare generică) este implementată prin șabloane de clasă și funcție. Cu toate acestea, esența ambelor metodologii este legată doar indirect de tehnologiile specifice de implementare. Mai formal, OOP se bazează pe polimorfismul de subtip , în timp ce OP se bazează pe polimorfismul parametric . În alte limbi, ambele pot fi implementate diferit. De exemplu, multimetodele din CLOS au o semantică similară polimorfismului parametric.
Masser și Stepanov disting următoarele etape în rezolvarea problemei conform metodologiei OP:
Minimizarea și încadrarea urmăresc să creeze o structură astfel încât algoritmii să fie independenți de tipurile de date specifice. Această abordare se reflectă în structura bibliotecii STL . [5]
O abordare alternativă pentru definirea programării generice, care poate fi numită programare generică de tip de date , a fost propusă de Richard Bird și Lambert Meertens . În el, structurile de tip de date sunt parametri ai programelor generice. Pentru a face acest lucru, în limbajul de programare este introdus un nou nivel de abstractizare, și anume parametrizarea față de clase de algebre cu semnătură variabilă . Deși teoriile ambelor abordări sunt independente de limbajul de programare, abordarea Musser-Stepanov, care pune accent pe analiza conceptuală, a făcut din C++ principala sa platformă, în timp ce programarea cu tipuri de date generice este folosită aproape exclusiv de Haskell și variantele sale [6] .
Instrumentele de programare generice sunt implementate în limbaje de programare sub forma unor mijloace sintactice care fac posibilă descrierea datelor (tipuri de date) și a algoritmilor (proceduri, funcții, metode) parametrizați pe tipuri de date. Pentru o funcție sau un tip de date, parametrii de tip formal sunt descriși în mod explicit . Această descriere este generalizată și nu poate fi utilizată direct în forma sa originală.
În acele locuri din program în care este utilizat un tip generic sau o funcție, programatorul trebuie să specifice în mod explicit parametrul de tip real care specifică declarația. De exemplu, o procedură generică de schimbare a două valori poate avea un parametru de tip care specifică tipul de valori pe care le schimbă. Când programatorul trebuie să schimbe două valori întregi, el apelează procedura cu parametrul de tip „ întreg ” și doi parametri - numere întregi, când două șiruri - cu parametrul de tip „ șir " și doi parametri - șiruri. În cazul datelor, un programator poate, de exemplu, să descrie un tip generic „ listă ” cu un parametru de tip care specifică tipul de valori stocate în listă. Apoi, atunci când descrie liste reale, programatorul trebuie să specifice un tip generic și un parametru de tip, obținând astfel orice listă dorită folosind aceeași declarație.
Când un compilator întâlnește un apel la un tip generic sau o funcție, efectuează procedurile necesare de verificare a tipului static , evaluează posibilitatea unei instanțieri date și, dacă este pozitiv, generează cod, înlocuind parametrul de tip real în locul parametrului de tip formal. în descrierea generică. Desigur, pentru utilizarea cu succes a descrierilor generice, tipurile efective de parametri trebuie să îndeplinească anumite condiții. Dacă o funcție generică compară valori ale unui parametru de tip, orice tip de beton utilizat în ea trebuie să suporte operații de comparare, dacă atribuie variabilelor valori ale unui parametru de tip, tipul de beton trebuie să asigure o atribuire corectă.
În C++, programarea generică se bazează pe conceptul de „șablon”, notat cu cuvântul cheie șablon . Este utilizat pe scară largă în biblioteca standard C++ (vezi STL ), precum și în bibliotecile de la terți , Loki . O mare contribuție la apariția instrumentelor avansate de programare generică în C++ a fost adusă de Alexander Stepanov .
De exemplu, să dăm un șablon (generalizare) unei funcții care returnează valoarea mai mare a două.
// Șablon de descriere a șablonului funcției < nume de tip T > T max ( T x , T y ) { dacă ( x < y ) returnează y ; altfel întoarce x ; } ... // Aplicând funcția dată de șablon int a = max ( 10 , 15 ); ... dublu f = max ( 123,11 , 123,12 ); ...sau un șablon (generalizare) a unei clase de listă legată:
șablon < classT > _ Lista clasei { /* ... */ public : void Add ( const T & Element ); bool Find ( const T & Element ); /* ... */ };Haskell oferă programare generică de tip de date. În exemplul următor a , o variabilă de tip parametric.
date Lista a = Nil | Cons a ( Lista a ) lungime :: List a -> Int lungime Nil = 0 lungime ( Cons _ tl ) = 1 + lungime tlExemplu de calcul:
lungime ( Cons 1 ( Cons 2 Nil )) == 2Java a furnizat generice care sunt bazate sintactic pe C++ începând cu J2SE 5.0. Acest limbaj are generice sau „containere de tip T” – un subset de programare generică.
Pe platforma .NET au apărut instrumente de programare generice în versiunea 2.0.
// Declarația unei clase generice. public class GenericList < T > { void Add ( T input ) { } } class TestGenericList { private class ExampleClass { } static void Main () { GenericList < int > list1 = new GenericList < int >(); GenericList < șir > list2 = new GenericList < șir >(); GenericList < ExampleClass > list3 = new GenericList < ExampleClass >(); } }Un exemplu de generare recursivă bazată pe șabloane D :
// http://digitalmars.com/d/2.0/template.html template Foo ( T , R ...) // T este un tip, R este un set de tipuri { void Foo ( T t , R r ) { scrieln ( t ); static if ( r . lungime ) // dacă mai multe argumente Foo ( r ); // faceți restul argumentelor } } void main () { Foo ( 1 , 'a' , 6.8 ); } /++++++++++++++++ imprimări: 1 la 6,8 +++++++++++++++/Suportul pentru programarea generică de către compilatorul Free Pascal a fost disponibil începând cu versiunea 2.2 în 2007 [7] . În Delphi - din octombrie 2008 . Suportul de bază pentru clasele generice a apărut pentru prima dată în Delphi 2007 .NET în 2006 , dar a afectat doar .NET Framework . Suport mai complet pentru programarea generică a fost adăugat în Delphi 2009 . Clasele generice sunt de asemenea acceptate în Object Pascal în sistemul PascalABC.NET .