Moștenirea multiplă
Moștenirea multiplă este o proprietate acceptată de o parte a limbajelor de programare orientate pe obiecte , când o clasă poate avea mai multe superclase (clasa părinte directă), interfețele acceptă moștenirea multiplă în multe limbaje de programare. Acest concept este o extensie a „moștenirii simple (sau unice) ” ( moștenire unică în engleză ), în care o clasă poate moșteni doar de la o superclasă.
Limbile care acceptă moștenirea multiplă includ: Io , Eiffel , C++ , Dylan , Python , unele implementări de clasă JavaScript (de exemplu , dojo .declare ), Perl 6 , Curl , Common Lisp (mulțumită CLOS ), OCaml , Tcl (mulțumită Incremental ). Tcl ) [1] , precum și Object REXX (datorită utilizării claselor mixin ).
Prezentare generală
Moștenirea multiplă permite unei clase să moștenească funcționalitatea de la multe alte clase, cum ar fi o clasă StudentMusicianpoate moșteni de la class Person, class Musicianși class Worker, care pot fi abreviate ca:
StudentMusician : Person, Musician, Worker.
Incertitudinea în moștenirea multiplă, ca în exemplul de mai sus, apare dacă, de exemplu, o clasă Musicianmoștenește din clase Personși Worker, iar clasa Worker, la rândul său, moștenește din Person; o situație similară se numește moștenire în formă de diamant . Astfel, obținem următoarele reguli:
Muncitor : Persoană
Muzician : Persoană, Muncitor
StudentMusicician : Persoană, Muzician, Muncitor
Dacă compilatorul se uită la clasa StudentMusician, atunci trebuie să știe dacă caracteristicile claselor ar trebui combinate sau ar trebui să fie separate. De exemplu, ar fi logic să atașăm „Vârsta” (vârsta) clasei Persoană la clasa StudentMusician. Vârsta unei persoane nu se schimbă dacă o considerați Persoană (persoană), Muncitor (lucrător) sau Muzician (muzician). Cu toate acestea, ar fi destul de logic să se separe proprietatea „Nume” în clasele Persoană și Muzician dacă folosesc un nume de scenă care este diferit de numele lor real. Opțiunile de unire și împărțire sunt perfect valabile pentru fiecare dintre propriile contexte și numai programatorul știe care opțiune este corectă pentru clasa proiectată.
Limbile au diferite moduri de a trata astfel de probleme de moștenire imbricate, de exemplu:
- Eiffel oferă programatorului capacitatea de a combina sau împărți în mod explicit membrii moșteniți din superclase. Eiffel va îmbina automat elementele dacă au același nume și aceeași implementare. Autorul clasei are capacitatea de a redenumi membrii moșteniți pentru a-i separa. În plus, Eiffel vă permite să remoșteniți în mod explicit fișierulA: B, B .
- C++ cere programatorului să specifice ce membru al clasei părinte trebuie utilizat, adică „Worker::Person.Age”. C++ nu acceptă moștenirea repetabilă în mod explicit, deoarece nu există nicio modalitate de a determina ce superclasă să folosească (vezi critică ). C++ permite, de asemenea, crearea unei singure instanțe a unei clase multiple datorită mecanismului de moștenire virtuală (de exemplu, " Worker::Person" și " Musician::Person" se vor referi la același obiect).
- Perl folosește o listă de clase din care să moștenească în această ordine. Compilatorul folosește prima metodă pe care o găsește atunci când caută în profunzime lista de superclase sau folosește liniarizarea C3 a ierarhiei claselor. Diverse extensii oferă scheme alternative de compunere a claselor.
- Python are suport sintactic pentru moștenirea multiplă, iar ordinea claselor de bază este determinată de algoritmul de liniarizare C3 [2] .
- Sistemul de obiecte Common Lisp oferă un control complet al metodelor de combinare de către programator, iar dacă acest lucru nu este suficient, atunci Protocolul Metaobject (Protocolul Metaobject) oferă programatorului posibilitatea de a modifica moștenirea, managementul dinamic , implementarea clasei și alte mecanisme interne fără teama de a afecta stabilitatea sistemului.
- Logtalk acceptă atât interfețele, cât și implementarea moștenirii multiple prin furnizarea unei declarații de metodă alias care acceptă atât redenumirea, cât și accesul la metode care ar putea să nu fie disponibile din cauza mecanismului de rezolvare a conflictelor.
- Curl permite numai clasele care sunt marcate în mod explicit ca re-moștenite. Clasele accesibile trebuie să definească un constructor secundar pentru fiecare constructor de clasă normală . Constructorul obișnuit este apelat mai întâi, starea clasei accesibile este inițializată de constructorul subclaselor, iar constructorul secundar este apelat pentru toate celelalte subclase.
- Ocaml alege ultima definiție de potrivire din lista de moștenire a clasei pentru a determina metoda de implementare de utilizat în caz de incertitudine. Pentru a suprascrie comportamentul implicit, pur și simplu furnizați o metodă care să fie apelată atunci când definiți clasa preferată.
- Tcl permite existența mai multor clase părinte - secvența acestora afectează rezoluția numelor membrilor clasei. [3]
- Delphi din versiunea 2007 vă permite să implementați parțial moștenirea multiplă folosind ajutoare de clasă (Ajutoare de clasă) .
Smalltalk , C# , Objective-C , Java , Nemerle și PHP nu permit moștenirea multiplă, ceea ce evită multe incertitudini. Cu toate acestea, ele, pe lângă Smalltalk, permit claselor să implementeze mai multe interfețe . În plus, PHP și Ruby vă permit să emulați moștenirea multiplă prin utilizarea mixin-urilor (trăsături în PHP și mixin în Ruby), care, ca și interfețele, nu sunt clase cu drepturi depline. Moștenirea multiplă a interfețelor vă permite să extindeți capabilități limitate.
Critica
Moștenirea multiplă a fost criticată pentru următoarele probleme în unele limbi, în special C++:
- ambiguitatea semantică este adesea reprezentată colectiv ca problema diamantului [4] .
- nu există nicio posibilitate de moștenire multiplă directă dintr-o clasă.
- ordinea moștenirii modifică semantica clasei. Constructorul clasei copil apelează constructorii părinților imediati, iar aceștia, la rândul lor, numesc constructorul bunicului. Cu toate acestea, obiectul bunic există într-o singură instanță și nu poate fi construit de două ori, așa că apelarea constructorului bunic numai de către constructorul primei clase părinte din lista de moștenire va funcționa.
Moștenirea multiplă în limbaje cu constructori în stil C++/Java exacerba problema moștenirii constructorului și a secvențelor constructoarelor, creând astfel probleme de mentenanță și extensibilitate în acele limbi. Obiectele aflate în relații de moștenire cu metode de construcție semnificativ diferite sunt destul de greu de implementat în paradigma secvenței constructorului.
Cu toate acestea, există limbi care se ocupă de aceste aspecte tehnice (de exemplu, Eiffel ).
Există o părere că moștenirea multiplă este un concept greșit, generat de o analiză și design greșit. În special, următoarea opțiune de proiectare este valabilă pentru exemplul de mai sus. Clasa Persoană include unul sau mai multe obiecte din clasa Profesie. Clasele Studenți și Muzician moștenesc din Profesie. Astfel, StudentMusician va fi reprezentat de un obiect din clasa Persoana care contine obiecte din clasa Student si Muzician. În mod formal, moștenirea multiplă poate fi inginerie inversă prin introducerea unei clase care este o „metaclasă” a claselor din care urmează să aibă loc moștenirea multiplă. În exemplul de mai sus, o astfel de metaclasă este Profession - o profesie.
Note
- ↑ Tcl Advocacy . Consultat la 2 decembrie 2009. Arhivat din original pe 22 septembrie 2010. (nedefinit)
- ↑ David M. Beazley. Referință esențială Python . — ediția a IV-a. - Addison-Wesley Professional, 2009. - S. 119 -122. — 717 p. — ISBN 978-0672329784 .
- ↑ Tcl Manual: clasa . Consultat la 2 decembrie 2009. Arhivat din original la 4 aprilie 2009. (nedefinit)
- ↑ Trăsături: Unități de Comportament Composable . Preluat la 2 decembrie 2009. Arhivat din original la 9 august 2017. (nedefinit)
Link -uri
Literatură