Moștenire (ing. moștenire ) - conceptul de programare orientată pe obiecte , conform căruia un tip de date abstract poate moșteni datele și funcționalitatea unui tip existent, facilitând reutilizarea componentelor software .
În programarea orientată pe obiecte , începând cu Simula 67 , tipurile de date abstracte sunt numite clase .
Superclasă ( ing. super clasă ), clasă părinte ( ing. parent class ), strămoș, părinte sau superclasă - o clasă care produce moștenire în subclase, adică o clasă de la care moștenesc alte clase. O superclasă poate fi o subclasă, o clasă de bază, o clasă abstractă și o interfață.
Subclasa ( ing. subclasa ), clasa derivata ( ing. clasa derivata ), clasa copil ( ing. clasa copil ), clasa descendenta, clasa succesoare sau clasa de implementare - o clasa mostenita de la o superclasa sau interfata, adica o clasa definita prin mostenire de la altă clasă sau mai multe astfel de clase. O subclasă poate fi o superclasă.
O clasă de bază este o clasă care se află în vârful ierarhiei de moștenire a clasei și în partea de jos a arborelui subclaselor, adică nu este o subclasă și nu moștenește de la alte superclase sau interfețe. Clasa de bază poate fi o clasă abstractă și o interfață. Orice clasă non-bază este o subclasă.
O interfață este o structură care definește o interfață de clasă pură constând din metode abstracte. Interfețele participă la ierarhia de moștenire a claselor și interfețelor.
O superinterfață ( ing. super interfață ) sau o interfață strămoșă este un analog al unei superclase din ierarhia moștenirii, adică este o interfață care moștenește în subclase și subinterfețe.
O interfață descendentă, o interfață derivată sau o interfață derivată este un analog al unei subclase din ierarhia de moștenire a interfețelor, adică este o interfață moștenită de la una sau mai multe superinterfețe.
O interfață de bază este echivalentul unei clase de bază în ierarhia de moștenire a interfețelor, adică este interfața din vârful ierarhiei de moștenire.
O ierarhie de moștenire sau ierarhie de clasă este un arbore ale cărui elemente sunt clase și interfețe.
Moștenirea este un mecanism de reutilizare a codului (reutilizarea codului englez ) și contribuie la extinderea independentă a software -ului prin clase deschise (clase publice engleze) și interfețe (interfețe engleze). Setarea unei relații de moștenire între clase generează o ierarhie de clasă.
Moștenirea este adesea identificată cu polimorfismul de subtipizare :
În ciuda observației de mai sus, moștenirea este un mecanism utilizat pe scară largă pentru stabilirea unei relații is -a . Unele limbaje de programare sunt de acord cu moștenirea și polimorfismul subtipului (mai ales limbaje tipizate static , cum ar fi C++ , C# , Java și Scala ), în timp ce altele împărtășesc conceptele de mai sus.
Moștenirea - chiar și în limbaje de programare care acceptă utilizarea moștenirii ca mecanism pentru polimorfismul subtipului - nu garantează polimorfismul comportamental subtipului; vezi: „Principiul substituției” de Barbara Liskov .
Moștenirea „simple”, numită uneori moștenire unică, descrie relația dintre două clase, dintre care una o moștenește pe cealaltă. Multe clase pot deriva dintr-o singură clasă, dar chiar și așa, acest tip de relație rămâne moștenire „simple”.
Clase abstracte și crearea de obiectePentru unele limbaje de programare, următorul concept este valabil.
Există clase „abstracte” (declarate ca atare în mod arbitrar sau din cauza metodelor abstracte care le sunt atribuite ); ele pot fi descrise ca având câmpuri și metode . Crearea de obiecte (instanțe) înseamnă concretizare , aplicabilă numai claselor non-abstracte (inclusiv descendenților neabstracti ai celor abstracte), ai căror reprezentanți, ca urmare, vor fi obiectele create.
Exemplu: Să fie abstractă clasa de bază „Angajat al Universității ”, din care sunt moștenite clasele „ Student postuniversitar ” și „ Profesor ”. Câmpurile și funcțiile comune ale claselor (de exemplu, câmpul „Anul nașterii”) pot fi descrise în clasa de bază. Și programul va crea obiecte numai din clasele derivate: „Student postuniversitar” și „Profesor”; de obicei nu are sens să se creeze obiecte ale claselor de bază.
Cu moștenire multiplă, o clasă poate avea mai mult de un părinte. În acest caz, clasa moștenește metodele tuturor strămoșilor. Avantajul acestei abordări este o mai mare flexibilitate.
Moștenirea multiplă este implementată în C++ . Alte limbi care oferă această caracteristică includ Python și Eiffel . Moștenirea multiplă este acceptată în UML .
Moștenirea multiplă este o sursă potențială de erori care pot apărea din a avea aceleași nume de metode în strămoși. În limbile care sunt poziționate ca succesori ai C++ ( Java , C# și altele), s-a decis abandonarea moștenirii multiple în favoarea interfețelor . Aproape întotdeauna puteți face fără a utiliza acest mecanism. Cu toate acestea, dacă totuși a apărut o astfel de nevoie, atunci pentru a rezolva conflictele în utilizarea metodelor moștenite cu aceleași nume, este posibil, de exemplu, să aplicați operația de extensie a vizibilității - "::" - pentru a apela o anumită metodă a unui anumit părinte.
O încercare de a rezolva problema de a avea aceleași nume de metodă în strămoși a fost făcută în limba Eiffel , în care, atunci când descrieți o nouă clasă, este necesar să se indice în mod explicit membrii importați ai fiecărei clase moștenite și denumirea lor în clasa de copii.
Majoritatea limbajelor de programare orientate pe obiecte moderne ( C# , Java , Delphi și altele) acceptă capacitatea de a moșteni simultan de la o clasă strămoșească și de a implementa metode ale mai multor interfețe de către aceeași clasă. Acest mecanism vă permite să înlocuiți în mare măsură moștenirea multiplă - metodele de interfață trebuie redefinite în mod explicit, ceea ce elimină erorile atunci când moșteniți funcționalitatea acelorași metode ale diferitelor clase de strămoși.
Într-un număr de limbaje de programare, toate clasele, fie explicit, fie implicit, moștenesc de la o clasă de bază. Smalltalk a fost una dintre primele limbi care a folosit acest concept. Aceste limbaje includ și: Objective-C (clasă NSObject), Perl ( UNIVERSAL), Eiffel ( ANY), Java ( java.lang.Object), C# ( System.Object), Delphi ( TObject), Scala ( Any).
Moștenirea în C++ :
clasaA { }; // Clasa de bază clasa B : public A {}; // Clasa de moștenire publică C : protejată A {}; // Clasa de moștenire protejată Z : privat A {}; // Moștenire privatăExistă trei tipuri de moștenire în C++ : publică , protejată , privată . Specificatorii de acces ai membrilor clasei de bază se modifică în descendenți după cum urmează:
Unul dintre principalele avantaje ale moștenirii publice este că un pointer către clase derivate poate fi implicit convertit într-un pointer către clasa de bază, așa că pentru exemplul de mai sus, puteți scrie:
A * a = nouB ( );Această caracteristică interesantă deschide posibilitatea identificării dinamice a tipului (RTTI).
Pentru a utiliza mecanismul de moștenire în Delphiclass , trebuie să specificați clasa strămoșului în declarația de clasă între paranteze :
Strămoş:
TAncestor = clasa privat protejat public // procedura procedura virtuala VirtualProcedure ; virtual ; abstract ; procedura StaticProcedure ; sfârşitul ;Moştenitor:
TDescendant = clasă ( TAncestor ) privat protejat public // Procedura virtuală de înlocuire a procedurii VirtualProcedure ; suprascrie ; procedura StaticProcedure ; sfârşitul ;Absolut toate clasele din Delphi sunt descendenți ai TObject. Dacă nu este specificată o clasă de strămoș, atunci se presupune că noua clasă este un descendent direct al TObject.
Moștenirea multiplă în Delphi nu este acceptată inițial în principiu, cu toate acestea, pentru cei care nu se pot descurca fără ea, există încă astfel de oportunități, de exemplu, prin utilizarea claselor de ajutor (Сlass Helpers).
Python acceptă atât moștenirea unică, cât și moștenirea multiplă. La accesarea unui atribut, vizualizarea claselor derivate are loc în ordinea ordinii de rezoluție a metodei ( MRO ) [1] .
clasa Ancestor1 ( obiect ): # Ancestor-1 def m1 ( self ): trece clasa Ancestor2 ( obiect ): # Ancestor-2 def m1 ( self ): trece class Descendent ( Ancestor1 , Ancestor2 ): # Descendent def m2 ( self ): trece d = Descendent () # Instanță tipărită d . __clasa__ . __mro__ # Ordinea rezoluției metodei: ( < clasa ' __main__ . Descendent '>, <clasa ' __principal__ . Strămoș1 '>, <clasa ' __principal__ . Strămoș2 '>, <tip ' obiect '>)Începând cu Python 2.2, clasele „clasice” și clasele „noile” coexistă în limbaj. Aceștia din urmă sunt moștenitori object. Clasele „clasice” vor fi acceptate până la versiunea 2.6, dar eliminate din limbaj în Python 3.0.
Moștenirea multiplă este utilizată în Python, în special, pentru a introduce clase mix -in în clasa principală .
Pentru a utiliza mecanismul de moștenire în PHPextends , este necesar să specificați cuvântul și numele clasei strămoși după numele clasei succesoare declarate în declarația clasei :
clasa Descendent extinde Strămoș { }Dacă clasa derivată se suprapune cu metodele strămoșilor, metodele strămoșilor pot fi accesate folosind parent:
class A { function example () { echo "Metoda A::example() numit.<br /> \n " ; } } clasa B extinde A { function example () { echo "Metoda B::example() numit.<br /> \n " ; parinte :: exemplu (); } }Este posibil să împiedicați o clasă derivată să depășească metodele unui strămoș; pentru a face acest lucru, trebuie să specificați cuvântul cheie final:
class A { final function example () { echo "Metoda A::example() numit.<br /> \n " ; } } clasa B extinde A { function example () { //va arunca o eroare parent :: example (); //și nu se va executa niciodată } }Pentru a face referire la constructorul clasei părinte în timpul moștenirii, este necesar ca clasa copil să specifice în constructor parent::__construct();[2]
Interfața declară metode care vor fi vizibile în afara clasei (publice).
Metodele interne pot fi implementate fără interfață. Pentru a declara proprietăți suplimentare, utilizați extensia-interfață în fișierul de implementare.
Toate metodele din Objective-C sunt virtuale.
Un exemplu de moștenire de la o clasă și două interfețe :
public class A { } public interface I1 { } public interface I2 { } public class B extinde A implementează I1 , I2 { }O directivă finaldintr-o declarație de clasă face imposibilă moștenirea din ea.
Un exemplu de moștenire de la o clasă și două interfețe :
public class A { } public interface I1 { } public interface I2 { } public class B : A , I1 , I2 { }Moștenirea din clasele tipizate se poate face prin specificarea unui tip fix sau prin transferul unei variabile de tip într-o clasă moștenită:
clasă publică A < T > { } clasă publică B : A < int > { } clasă publică B2 < T > : A < T > { }De asemenea, este posibil să moșteniți clase imbricate din clasele care le conțin:
clasa A // clasa implicită A este internă, nu publică clasa B nu poate fi publică { clasa B : A { } }O directivă sealeddintr-o declarație de clasă face imposibilă moștenirea din ea. [3]
Clasa Parenteste strămoșul clasei a Childcărei metodă este suprascrisă public_method.
copil = Copil . copil nou._ _ public_method # => „Metodă publică redefinită” copil . call_private_method # => „Metoda privată a strămoșilor: metoda privată”Metodele private ale unui strămoș pot fi apelate de la descendenți.
Clasa Parenteste strămoșul clasei a Childcărei metodă este suprascrisă publicMethod.
JavaScript folosește moștenirea prototipală.
În C++ , constructorii sunt chemați secvențial în timpul moștenirii de la cel mai vechi strămoș la cel mai recent copil și invers, destructorii sunt chemați de la cel mai recent copil la cel mai vechi strămoș.
classFirst _ { public : First () { cout << ">>Primul constructor" << endl ; } ~ First () { cout << ">>First destructor" << endl ; } }; clasa a doua : public Prima { public : Second () { cout << ">Al doilea constructor" << endl ; } ~ Second () { cout << ">Secund destructor" << endl ; } }; clasa a treia : public a doua { public : Third () { cout << "Al treilea constructor" << endl ; } ~ Third () { cout << "Third destructor" << endl ; } }; // executarea codului Third * th = new Third (); șterge th ; // rezultat rezultat /* >>Primul constructor >Al doilea constructor Al treilea constructor Al treilea distrugător >Al doilea distrugător >>Primul distrugător */