Moștenirea diamantelor
Moștenirea diamantului este o situație în limbajele de programare orientate pe obiecte cu suport pentru moștenirea multiplă , când două clase și moștenesc de la , iar o clasă moștenește de la ambele clase și . Cu această schemă de moștenire, poate apărea ambiguitatea: dacă un obiect de clasă apelează o metodă definită în clasă (și această metodă nu a fost suprascrisă în clasă ), iar clasele suprascriu această metodă în felul lor, atunci din ce clasă ar trebui să fie moștenit: sau ?
BC ADBCDADBCBC
De exemplu, în domeniul dezvoltării interfeței grafice , o clasă Button(„Button”) poate moșteni simultan dintr-o clasă Rectangle(„Dreptunghi”, pentru aspect) și dintr-o clasă Clickable(„Disponibil pentru clicuri de mouse”, pentru implementarea funcționalității/prelucrarea intrărilor). ), Rectangleși Clickablemoștenesc din clasă Object("Obiect"). Dacă apelați metoda equals(“Egal cu”) pe obiect Button, iar clasa Buttonnu are o astfel de metodă, dar clasa Objectare o metodă equalssuprascrisă în felul său atât în clasă, cât Rectangleși în Clickable, atunci care metodă ar trebui apelată?
Problema diamantului și-a primit numele de la forma diagramei de moștenire de clasă în această situație . În acest articol, clasa este desemnată în partea de sus, clasele și sunt enumerate individual mai jos și conectate la ambele în partea de jos, formând un romb .
ABCD
Hotărâri
Diverse limbaje de programare rezolvă problema moștenirii diamantelor în următoarele moduri:
- C++ nu creează moștenire de diamant în mod implicit: compilatorul tratează fiecare cale de moștenire separat, ceea ce va avea ca rezultat ca obiectul Dsă conțină de fapt două subobiecte diferite și va trebui să specificați calea de moștenire ( sau ) Aatunci când utilizați membri . Pentru a genera o structură de moștenire în formă de romb, este necesar să folosiți moștenirea virtuală a unei clase pe mai multe căi de moștenire: dacă ambele moșteniri de la to și de la to sunt marcate cu un specificator (de exemplu, ), C ++ va avea grijă de crearea unui singur subobiect într-un mod special , iar utilizarea membrilor va funcționa corect. Dacă moștenirea virtuală și nevirtuală sunt amestecate, atunci există un subobiect virtual și un subobiect nevirtual pentru fiecare cale de moștenire nevirtuală către . Când o metodă de clasă de bază virtuală este numită virtuală, se folosește așa-numita regulă de dominanță: compilatorul interzice un apel virtual la o metodă care a fost supraîncărcată pe mai multe căi de moștenire.AB::AC::AAABACvirtualclass B : virtual public AAAAAA
- Common Lisp încearcă să implementeze atât un comportament implicit rezonabil, cât și capacitatea de a-l schimba. În mod implicit, este selectată metoda cu cele mai specifice clase de argumente; apoi, metodele sunt selectate în ordinea în care sunt specificate clasele părinte când este definită subclasa. Cu toate acestea, programatorul este destul de liber să schimbe acest comportament specificând o ordine specială de rezoluție a metodei sau specificând o regulă pentru combinarea metodelor.
- Eiffel tratează această situație cu directivele și select, renameiar metodele părinte care sunt utilizate în descendenți sunt specificate în mod explicit. Acest lucru vă permite să partajați metodele clasei părinte în descendenți sau să le furnizați o copie separată a clasei părinte.
- Perl și Io gestionează moștenirea prin căutarea depth-first, în ordinea utilizată în definiția clasei. Clasa Bși strămoșii ei vor fi verificate înainte de clasă Cși strămoșii ei, astfel încât metoda în Ava fi moștenită de la B; lista de permisiuni - [ D, B, A, C]. Cu toate acestea, în Perl, acest comportament poate fi modificat folosind mrosau alte module pentru a aplica liniarizarea C3 (ca în Python) sau alți algoritmi.
- În Python , problema diamantului a intrat în atenție în versiunea 2.3 odată cu introducerea claselor cu un strămoș comun object; pornind de la această versiune s-a decis crearea unei liste de rezoluții folosind liniarizarea C3 [1] . În cazul unui diamant, aceasta înseamnă căutarea în profunzime , începând din stânga ( D, B, A, C, A) și apoi eliminând din listă toate, cu excepția ultimei includeri a fiecărei clase care se repetă în listă. Prin urmare, ordinea finală de rezoluție arată astfel: [ D, B, C, A].
- Lista de rezoluții a lui Scala este creată în mod similar cu Python, dar printr-o căutare în profunzime, începând din dreapta. Prin urmare, lista de rezoluție preliminară a diamantului este [ D, C, A, B, A], iar după eliminarea repetărilor este [ D, C, B, A].
- JavaFX Script , începând cu versiunea 1.2, permite moștenirea multiplă prin utilizarea mixin -urilor . În cazul unui conflict, compilatorul interzice utilizarea directă a variabilelor sau funcțiilor nedefinite. Fiecare membru moștenit va fi în continuare accesibil prin turnarea obiectului la mixin-ul dorit, cum ar fi (individual as Person).printInfo();.
Alte exemple
Limbajele care permit doar moștenirea simplă (cum ar fi Ada , Objective-C , PHP , C# , Delphi / Free Pascal și Java ) asigură moștenirea multiplă a interfețelor (numite protocoale în Objective-C). Interfețele sunt în esență clase de bază abstracte, toate metodele fiind, de asemenea, abstracte și în care nu există câmpuri. Astfel, problema nu se pune, deoarece va exista întotdeauna o singură implementare a unei anumite metode sau proprietăți, fără a permite ambiguitatea să apară.
Problema diamantelor nu se limitează la moștenire. De asemenea, apare în limbaje precum C și C++ atunci când fișierele de antet A, B, C și D, precum și anteturile separate precompilate create din B și C, sunt conectate (folosind instrucțiunile #include) unul la altul în modelul romboidale. prezentat mai sus.. Dacă aceste două anteturi precompilate sunt combinate, declarațiile din A sunt duplicate și directiva de protecție a conexiunii #ifndef devine ineficientă. De asemenea, problema se găsește la îmbinarea stivelor de subrutine ; de exemplu, dacă A este o bază de date și B și C sunt cache , atunci D poate cere atât B cât și C să comite ( COMMIT ) tranzacția, rezultând apeluri de confirmare duplicate către A.
Note
- ↑ Ordinul de rezoluție a metodei Python 2.3 . Consultat la 15 mai 2010. Arhivat din original la 12 aprilie 2012.
Literatură
- Eddy Truyen; Wouter Joosen, Bo Jørgensen, Petrus Verbaeten. O generalizare și soluție la problema dilemei strămoșilor comune în sistemele de obiecte bazate pe delegare // Proceedings of the 2004 Dynamic Aspects Workshop : journal. - 2004. - Nr. 103-119 .