Programarea prototipului este un stil de programare orientată pe obiecte în care nu există un concept de clasă , iar moștenirea se face prin clonarea unei instanțe existente a unui obiect - un prototip .
Exemplul canonic al unui limbaj orientat spre prototip este Self . În viitor, acest stil de programare a început să câștige popularitate și a stat la baza unor astfel de limbaje de programare precum JavaScript , Lua , Io , REBOL etc.
Î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 acelei clase. O instanță este un purtător de date, adică are o stare care se modifică în funcție de comportamentul definit de clasă.
Susținătorii programării prototipale susțin adesea că limbajele bazate pe clasă duc la un accent excesiv asupra taxonomiei claselor și a relațiilor dintre ele. În schimb, prototiparea se concentrează pe comportamentul unui număr (mic) de „modele”, care sunt apoi clasificate ca obiecte „de bază” și utilizate pentru a crea alte obiecte. Multe sisteme orientate spre prototipuri acceptă schimbarea prototipurilor în timpul execuției, în timp ce doar o mică parte din sistemele orientate spre clasă (de exemplu, Smalltalk , Ruby ) permit modificarea dinamică a claselor.
Deși marea majoritate a sistemelor bazate pe prototipuri se bazează pe limbaje interpretate tip dinamic, este posibil din punct de vedere tehnic să adăugați prototipuri și limbilor verificate static. Limbajul Omega este un exemplu de astfel de sistem.
În limbajele orientate spre clasă, o nouă instanță este creată prin apelarea constructorului de clasă (poate cu un set de parametri). Instanța rezultată are structura și comportamentul codificate de clasa sa.
Sistemele de prototipare oferă două metode pentru a crea un obiect nou: clonarea unui obiect existent sau crearea unui obiect de la zero . Pentru a crea un obiect de la zero, programatorului i se oferă mijloace sintactice pentru a adăuga proprietăți și metode obiectului. În viitor, o copie completă a acesteia - o clonă - poate fi obținută din obiectul rezultat. În timpul procesului de clonare, copia moștenește toate caracteristicile prototipului său, dar din acel moment devine independentă și poate fi schimbată. În unele implementări, copiile stochează referințe la obiecte prototip, delegându -le o parte din funcționalitatea acestora; în timp ce schimbarea prototipului poate afecta toate copiile acestuia. În alte implementări, noile obiecte sunt complet independente de prototipurile lor. Ambele cazuri sunt discutate mai jos.
//Un exemplu de moștenire în programarea prototipală //pe exemplul limbajului JavaScript //Creează un nou obiect let foo = { nume : "foo" , one : 1 , two : 2 }; //Crearea unui alt obiect nou let bar = { two : "two" , three : 3 }; bar . __proto__ = foo ; // foo este acum prototipul pentru bar //Dacă acum încercăm să accesăm câmpurile foo din bar //va funcționa bar . unu // Egal cu 1 //Câmpurile personalizate sunt disponibile și în bara . trei // Egal cu 3 //Câmpurile personalizate au o prioritate mai mare decât bara câmpurilor prototip . doi ; // Este egal cu „doi”În limbajele orientate spre prototip care folosesc delegarea , runtime-ul este capabil să trimită apeluri de metodă (sau să caute datele potrivite) pur și simplu urmând lanțul de pointeri de delegare (de la un obiect la prototipul său), până când se realizează o potrivire. Spre deosebire de relația clasă-instanță, relația prototip-copil nu necesită ca obiectele descendente să păstreze asemănarea structurală cu prototipul lor. În timp, se pot adapta și îmbunătăți, dar nu este nevoie să reproiecteze prototipul. Este important să puteți adăuga / șterge / modifica nu numai date, ci și funcții, în timp ce funcțiile se dovedesc a fi și obiecte de primul nivel . Ca rezultat, majoritatea limbajelor orientate spre prototip se referă la datele și metodele obiectului ca „sloturi” (celule).
În prototiparea „pură” – numită și cascadă și introdusă în Kevo – obiectele clonate nu stochează referințe la prototipurile lor. Prototipul este copiat unu-la-unu, cu toate metodele și atributele, iar copiei i se atribuie un nou nume (referință). Seamănă cu mitoza celulelor biologice.
Printre avantajele acestei abordări se numără și faptul că creatorul copiei o poate schimba fără teama de efecte secundare printre ceilalți descendenți ai strămoșului său. Costul de calcul al dispecerării este, de asemenea, redus drastic, deoarece nu este nevoie să parcurgeți întregul lanț de posibili delegați în căutarea unui slot adecvat (metodă sau atribut).
Dezavantajele includ dificultăți în propagarea modificărilor în sistem: modificarea unui prototip nu schimbă imediat și automat toți descendenții săi. Cu toate acestea, Kevo oferă mijloace suplimentare pentru publicarea modificărilor între mai multe obiecte pe baza asemănării lor („asemănarea familiei”), mai degrabă decât pe prezența unui strămoș comun, ceea ce este tipic pentru modelele cu delegare.
Un alt dezavantaj este că cele mai simple implementări ale acestui model duc la un consum de memorie crescut (comparativ cu modelul de delegare), întrucât fiecare clonă, până când este schimbată, va conține o copie a datelor sale prototip. Cu toate acestea, această problemă poate fi rezolvată prin separarea optimă a datelor neschimbate și utilizarea „ copie leneșe ” - care a fost folosită în Kevo.
Susținătorii modelelor de obiecte orientate spre clasă care critică abordarea prototipică se îngrijorează adesea de aceleași probleme pe care dactilografii statici se îngrijorează despre limbajele tipizate dinamic. În special, discuțiile se învârt în jurul unor subiecte precum corectitudinea , siguranța , predictibilitatea și eficiența programului .
În ceea ce privește primele trei puncte, clasele sunt adesea tratate ca tipuri (și într-adevăr sunt în majoritatea limbajelor orientate pe obiecte tipizate static), iar clasele ar trebui să ofere anumite convenții și să garanteze că instanțele se vor comporta într-un mod bine definit.
În ceea ce privește eficiența, declararea claselor simplifică foarte mult sarcina de optimizare a compilatorului , făcând atât metodele, cât și căutările de atribute pe instanțe mai eficiente. În cazul limbajului Self , o mare parte a timpului s-a petrecut dezvoltând tehnici de compilare și interpretare care să aducă performanța sistemelor bazate pe prototipuri mai aproape de concurenții lor orientați spre clasă. Lucrările ulterioare în această direcție, precum și progresul în teoria compilatoarelor JIT, au condus la faptul că în prezent distincția dintre abordările orientate spre clasă și abordările orientate spre prototip are un efect redus asupra eficienței codului rezultat. În special, Lua bazat pe prototip este una dintre cele mai rapide limbi interpretate și concurează direct cu multe dintre cele compilate, [1] iar traducătorul limbajului Lisaac generează cod ANSI C care este aproape la fel de bun ca nativ. [2]
În cele din urmă, poate cea mai frecventă critică împotriva programării prototipului este că comunitatea de dezvoltare de software nu este suficient de familiarizată cu aceasta, în ciuda popularității și omniprezentei JavaScript . În plus, deoarece sistemele bazate pe prototipuri sunt relativ noi și încă puține, tehnicile de dezvoltare care le utilizează nu s-au răspândit încă.
Tipuri de date | |
---|---|
Ininterpretabil | |
Numeric | |
Text | |
Referinţă | |
Compozit | |
abstract |
|
Alte | |
subiecte asemănătoare |