Herència (informàtica)

De la Viquipèdia, l'enciclopèdia lliure.
Saltar a la navegació Saltar a la cerca

En computació, l' herència és un dels conceptes fonamentals en l' objecte paradigma de programació . Consisteix en una relació que el llenguatge de programació , o el propi programador , estableix entre dues classes . Si la classe B hereta de la classe A , es diu que B és una subclasse d' A i A és una superclasse de B. Les designacions alternatives equivalents són classe pare o classe base per A i classe fill o classe derivada per B Depenent de la programació del llenguatge, l’herència pot ser una herència simple o única (cada classe pot tenir com a màxim una superclasse directa) o múltiple (cada classe pot tenir superclasses més directes).

En general, l’ús de l’herència dóna lloc a una jerarquia de classes; en llengües amb herència única, hi ha un arbre si hi ha una superclasse "arrel" única de la qual totes les altres són directament o indirectament subclasses (com la classe Object en el cas de Java ) o un bosc d'una altra manera; l’herència múltiple, d’altra banda, defineix una jerarquia de gràfics acíclics directa .

Interpretació

L’herència és una relació de generalització / especialització: la superclasse defineix un concepte general i la subclasse representa una variant específica d’aquest concepte general. Tota la teoria de l’herència en els llenguatges orientats a objectes es basa en aquesta interpretació. A més de ser una eina de modelatge important (i per tant també significativa en contextos diferents de la programació en sentit estricte, per exemple en UML ), l’herència té repercussions molt importants sobre la reutilització del programari.

És una relació

Per exemple, se li dóna un telefono classe si podrien derivar el cellulare la subclasse, ja que el telèfon és un cas particular del telèfon . Aquest tipus de relació també s’anomena relació is -a ("és-a"): "un mòbil és un telèfon".

La relació és una relació que ha de lligar una subclasse a la seva superclasse sovint es fa explícita fent referència a l'anomenat principi de substitució de Liskov , introduït el 1993 per Barbara Liskov i Jeannette Wing . Segons aquest principi, els objectes que pertanyen a una subclasse han de poder mostrar tots els comportaments i propietats que exhibeixen els que pertanyen a la superclasse, de manera que utilitzar-los en lloc d’aquesta última no alteri la correcció de la informació retornada pel programa. Per tal que la classe cellulare es pugui concebre com una subclasse de telefono , per exemple, és necessari que es pugui utilitzar un telèfon mòbil en tots els contextos en què es requereixi l'ús d'un telèfon.

Tant la relació is-a com el principi de Liskov no requereixen que la subclasse només presenti les característiques que exhibeix la superclasse. Per exemple, el fet que un telèfon mòbil també pugui enviar SMS no afecta el fet que es pugui substituir per un telèfon. Per tant, la subclasse pot presentar característiques addicionals en comparació amb la superclasse.
A més, també podria realitzar algunes de les seves funcionalitats de manera diferent, sempre que aquesta diferència no sigui observable des de l'exterior. Per exemple, un telèfon mòbil inicia o rep una trucada d'una manera tècnicament diferent a la d'un telèfon tradicional (mitjançant la xarxa GSM ), però fins i tot això no contradiu el principi de substituibilitat.

Violació del principi de substituibilitat

Malgrat tot, en general és tècnicament possible ampliar una classe infringint el principi de substituibilitat, ja que les regles imposades pel llenguatge de programació en ús no poden anar més enllà de la correcció formal del codi escrit i, possiblement, de la seva adhesió a determinades precondicions o postcondicions. En alguns casos, el principi es vulnera intencionadament [1] ; no obstant això, quan això passa, és aconsellable documentar la qüestió de manera adequada, per evitar que s'utilitzin les instàncies de la classe on es suposa que el principi de substitució esmentat és vàlid [1] .

Polimorfisme
Icona de la lupa mgx2.svg Mateix tema en detall: Polimorfisme (informàtica) .

Quan es respecta el principi de substituibilitat, es pot utilitzar l’herència per obtenir l’anomenat polimorfisme . Si s’utilitza bé, permet programes flexibles , en el sentit que permet escriure codi capaç de fer front a les necessitats i canvis futurs, que requereixin correccions mínimes i / o ben circumscrites.

Definició tècnica

La forma en què els llenguatges de programació gestionen les relacions d’herència segueix el significat que es dóna a l’herència com a relació és-és . Una classe B va declarar una subclasse d'una altra classe A

  • hereta (implícitament) totes les variables d'instància i mètodes d'A.
  • pot tenir variables o mètodes addicionals.
  • Pot redefinir els mètodes heretats de la A fins a la substitució , de manera que executin la mateixa operació conceptual de manera especialitzada.

El fet que la subclasse hereti totes les característiques de la superclasse té sentit a la llum del concepte de substituibilitat. De fet, en el paradigma orientat a objectes, una classe d’objectes es defineix per les seves característiques (atributs i mètodes). En conseqüència, seria fals afirmar que "un telèfon mòbil és un telèfon" si el mòbil no tenia totes les característiques definidores d'un telèfon (per exemple, un micròfon , un altaveu i la capacitat d'iniciar o rebre trucades).

Tot i això, el que s’ha dit no implica que es pugui garantir la substituïbilitat: s’ha de distingir conceptualment la relació classe-subclassa de la relació tipus-subtipus. En particular, el mecanisme primordial no garanteix que la semàntica del mètode de superclasse es mantingui inalterada a la subclasse. Tampoc es respecta la substitució quan s’utilitzen eines per ocultar la visibilitat dels mètodes ( limitació ).

Aplicacions de l'herència

L'herència es pot estudiar i descriure des de diversos punts de vista:

  • comportament dels objectes respecte a l’entorn extern;
  • estructura interna dels objectes;
  • jerarquia dels nivells d'herència;
  • impacte de l'herència en l' enginyeria de programari .

En general, per evitar confusions, és aconsellable abordar aquests aspectes per separat

Especialització

Icona de la lupa mgx2.svg El mateix tema en detall: Subtipus (informàtica) .

Un dels principals avantatges de l'herència és la capacitat de crear versions "especialitzades" de classes existents, és a dir, de crear-ne subtipus . Les construccions que permeten assolir l’herència no garanteixen l’especialització, que el programador ha de proporcionar definint la subclasse de la manera adequada, per tal de respectar el principi de substituibilitat.

Un altre mecanisme similar a l'especialització és l'especificació: es produeix quan una classe heretada declara posseir un determinat "comportament" sense implementar-lo realment: en aquest cas parlem d'una classe abstracta . Totes les classes "concretes" (és a dir, no elles mateixes abstractes) que hereten d'aquesta classe abstracta necessàriament han d'implementar aquest comportament particular "desaparegut".

Redefinició

Icona de la lupa mgx2.svg El mateix tema en detall: anul·lació .

Molts llenguatges de programació orientats a objectes permeten que una classe o objecte modifiqui la manera com s’implementa la seva pròpia funcionalitat heretada d’una altra classe (normalment un mètode). Aquesta característica s'anomena " anul·lació ". Davant la substitució, el mateix mètode tindrà un comportament diferent si s’invoca als objectes de la superclasse o als de la subclasse (almenys en el cas dels llenguatges que adopten un enllaç dinàmic ). Per exemple, donada una classe Quadrilatero que defineix alguns comportaments generals per a totes les figures geomètriques de 4 cares, la subclasse Rettangolo podria redefinir (és a dir, "anul·lar") aquells mètodes Quadrilatero que es poden reimplementar d'una manera més específica tenint en compte els rectangles d'especificitats ( per exemple, el càlcul de l'àrea es podria reescriure a la classe Rettangolo més facilitat i eficiència simplement com a producte dels laterals).

Extensió

Una altra raó per utilitzar l'herència és proporcionar una classe de dades o una funcionalitat addicionals. Normalment s’anomena extensió o subclassificació . A diferència del cas de l’especialització descrita anteriorment, amb l’extensió s’afegeixen noves dades o funcionalitats a la classe heretada, accessibles i utilitzables per totes les instàncies de la classe. L'extensió s'utilitza sovint quan no és possible o convenient afegir una nova funcionalitat a la classe base. La mateixa operació també es pot realitzar a nivell d'objecte, en lloc de la classe, per exemple mitjançant els anomenats patrons de decoració .

Reutilització del codi

Un dels principals avantatges d'utilitzar l'herència (sobretot combinat amb el polimorfisme) és que afavoreix la reutilització del codi . Una subclasse no només hereta (i, per tant, reutilitza) el codi de la superclasse: el polimorfisme també garanteix que tot el codi escrit prèviament per manipular objectes de superclasse també sigui implícitament capaç de manipular objectes de subclasse. Per exemple, un programa que sigui capaç de representar gràficament objectes de la classe Quadrilatero no necessitaria cap modificació per tractar de manera similar també objectes d’una possible classe Rettangolo .

Exemples

Suposem que un programa utilitza una classe Animale que conté dades per especificar, per exemple, si l'animal és viu, on és, quantes potes té, etc. a més d'aquestes dades, la classe també podria contenir mètodes per descriure com l'animal menja, beu, es mou, es fa companys, etc. Si volguéssim crear una classe de Mammifero , moltes d’aquestes característiques seguirien exactament igual que les dels animals genèrics, però algunes serien diferents. Per tant, diríem que els Mammifero són una subclasse de la classe Animale (o, al contrari, que Animale és la classe base –també anomenada classe Mammifero dels Mammifero ). L’important que cal tenir en compte és que per definir la nova classe no cal especificar de nou que un mamífer té les característiques normals d’un animal (on es troba, el fet que menja, beu, etc.), però és suficient afegir les característiques peculiars que distingeixen els mamífers d'altres animals (per exemple, que està cobert de pèl i té ubres, i redefinir les funcions que, tot i que són comunes a la resta d'animals, es manifesten de manera diferent, per exemple la manera de reproduir-se. següent exemple, escrit en Java , observeu dins del mètode riproduciti() la crida a super.riproduciti() , que és un mètode de la classe base que s'està redefinint. Per utilitzar paraules simples podríem dir que aquest mètode diu a " fes tot el que faria primer la classe base "seguit del codi que indica les" coses addicionals "que ha de fer la nova classe.

Java

 classe Mamífer estén Animal {
    Pèl de cabell ;
    Mamelles mamàries ;

    Mamífer reproduït () {
        Cries de mamífers;

        súper . reproduït ();
        if ( isFemale ()) {
            descendència = súper . parir ();
            descendència . lactància materna ( m_b );
        }
        carePioties ( descendència );
        tornar descendència ;
    }
}

A l'exemple següent, es declara una classe Employee amb alguns atributs comuns (variables). El constructor (Sub) es declara gràcies al qual es pot instanciar un objecte de la classe emprada. Les variables indicades amb "_" s'utilitzen per assegurar-vos que podeu introduir la validació de dades abans de passar els valors d'entrada a l'objecte. Tot seguit, però, la classe gerent hereta de la classe Empleat. Per tant, haurà obtingut implícitament (o més aviat heretat) tots els mètodes i funcions que hem declarat a la classe pare. En aquest exemple pràctic podem observar que, a més d’heretar les propietats de la classe dels empleats, la classe gestora implementa funcions i paràmetres exclusius.

VB.NET

 Empleat Públic Classe
nom privat com a Cadena
salari privat com a doble
matricola privada com a Corda
anys privats de servei com a enter
Public Sub New (n com seqüència, és tan Room, m es String, anuncis com nombre enter)
name = _Nom com a cadena  
salari = _salari com a doble 
matricola = _matricola as string 
yearsOfService = _ads com a enter 
        
Finalitzar sub
classe final

'La classe de gerent que va heretar de la classe d'empleats

Public Class Manager Hereta empleat
Nom privat: secretari com a cadena
Public Sub New (n com cordes, s com a doble, m AsString, anuncis com sencer)
MyBase . Novetat ( n , s , m , anuncis )
secretari = Cadena . buit
Finalitzar sub
  
Classe final

"Ldp"

Fulls d’estil

El concepte d’herència s’aplica, de manera més general, a qualsevol procés informàtic en què un determinat "context" rep certes "característiques " d'un altre context. Per exemple, en algunes aplicacions de processador de textos , els atributs estilístics del text, com ara la mida de la font , el disseny o el color, es poden heretar d’una plantilla o d’un altre document. L'usuari pot definir atributs per aplicar a alguns elements específics, mentre que tots els altres hereten els atributs d'una especificació de definició d'estil global. Per exemple, els anomenats fulls d'estil en cascada (CSS) són un llenguatge de definició d'estil àmpliament utilitzat en el disseny de pàgines web . De nou, alguns atributs estilístics es poden definir d'una manera específica, mentre que d'altres es reben "en cascada". Quan es consulten llocs web , per exemple, l'usuari pot decidir aplicar un estil definit per ell mateix per a la mida de les fonts a les pàgines, mentre que altres característiques, com el color i el tipus de tipus de lletra, es poden heretar del full d'estil general. del lloc.

Limitacions i alternatives

Un ús massiu de la tècnica de l’herència en el desenvolupament de programes pot tenir algunes contraindicacions i imposar algunes limitacions.

Suposem que tenim una classe de Persona que conté nom, adreça, número de telèfon, edat i sexe com a dades. Podem definir una subclasse de Persona , anomenada Studente , que conté la mitjana de les notes i els cursos cursats, i una altra subclasse de Persona , anomenada Impiegato , que conté la qualificació, el lloc de treball realitzat i el salari.

Algunes restriccions ja estan implícites en la definició d’aquestes jerarquies d’herències, algunes de les quals són útils, mentre que d’altres creen problemes:

Restriccions imposades per la programació basada en l'herència

  • Unicitat

En el cas d’una herència simple, una classe només pot heretar d’ una classe base. A l'exemple anterior, la Persona pot ser Studente i Impiegato , però no tots dos. L'herència múltiple resol parcialment aquest problema creant una classe StudenteImpiegato Impiegato que hereta tant de Studente com de Impiegato . Tanmateix, aquesta nova classe només pot heretar de la seva classe base una vegada: per tant, aquesta solució no resol el cas en què un "estudiant" té dues feines o assisteix a dues escoles.

  • Estaticitat

La jerarquia de l'herència d'un objecte queda "congelada" quan s'instancia l'objecte i no es pot canviar posteriorment. Per exemple, un objecte de la classe Studente no pot convertir-se en objecte Impiegato mentre manté les característiques de la seva classe base Persona [ poc clar ] .

  • Visibilitat

Quan un programa "client" té accés a un objecte, normalment també té accés a totes les dades d'un objecte pertanyent a la classe base. Fins i tot si la classe base no és de tipus "públic", el programa client pot crear objectes del seu tipus. Per tal que una funció llegeixi el valor mitjà d'un Studente s'ha de donar a aquesta funció la possibilitat d'accedir a totes les dades personals emmagatzemades a la classe base Persona .

Herència i rols

Un rol descriu una característica associada a un objecte basada en les interrelacions que aquest objecte té amb un altre objecte (per exemple: una persona amb el rol d' alumne assisteix a un curs escolar). L'herència es pot utilitzar per implementar aquestes relacions. En la programació orientada a objectes, aquestes dues tècniques de programació sovint s’utilitzen com a alternatives entre si. L’herència s’utilitza sovint per modelar rols. Per exemple, podeu definir un rol d' estudiant per a una persona realitzada definint una subclasse de persona . En qualsevol cas, ni la jerarquia de l’herència ni el tipus d’objectes poden canviar amb el pas del temps. Per aquest motiu, definir rols com a subclasses pot provocar la congelació dels rols quan es crea l'objecte. En el nostre exemple, Persona ja no podria canviar fàcilment el seu rol d’ estudiant a empleat , si les circumstàncies ho justifiquen.

Aquestes restriccions poden ser perjudicials, ja que dificulten la implementació dels canvis que puguin ser necessaris en el futur, ja que aquestes últimes només es poden introduir després de remodelar i actualitzar tot el projecte.

Per fer un ús correcte de l'herència, cal pensar en termes tan "generals" com sigui possible, de manera que els aspectes comuns a la majoria de les classes a instanciar es reuneixin "amb un factor comú" i s'insereixin en els seus respectius classes de pares. Per exemple, una classe base AspettiLegali es pot heretar tant de la classe Person com de la classe Firm per gestionar els problemes legals comuns a tots dos.

Per triar la tècnica més convenient a aplicar (projecte basat en rols o herència) val la pena preguntar-se si:

  • un mateix objecte ha de representar rols diferents i realitzar diferents funcions en diferents moments (disseny basat en rols);
  • diverses classes (tingueu en compte, classes, NO objectes) han de realitzar operacions comunes que es puguin agrupar i atribuir a una sola classe base (disseny basat en l'herència).

Una conseqüència important de la separació entre rols i classes pares és que el temps de compilació i el temps d' execució del codi objecte produït estan clarament separats. L’herència és clarament una construcció aplicada en temps de compilació, que no modifica l’estructura dels objectes durant el temps d’execució. De fet, els "tipus" d'objectes instanciats ja estan predeterminats durant el temps de compilació. Com ja s’ha indicat en els exemples anteriors, a l’hora de dissenyar la classe Person , sent un empleat un cas particular de persona, cal assegurar-se que la classe Person només contingui les funcionalitats i les dades comunes a totes les persones, independentment del context en què aquesta classe s’instancia. D'aquesta manera, esteu segur, per exemple, que en una classe de persona no s'utilitzarà mai el membre del treball , ja que no totes les persones tenen feina, o, com a mínim, no es garanteix a priori que s'instancia la classe de persona. només per crear objectes referibles a persones que tenen feina.

En el seu lloc, pensant des del punt de vista de la programació basada en rols, es podria definir un subconjunt de tots els objectes possibles de la persona que exerceixen el "rol" d'empleat. La informació necessària per definir les característiques del treball realitzat només s’inserirà en els objectes que exerceixin el paper d’empleat.

Una modelització orientada a objectes pot definir el propi treball com un rol, ja que un treball es pot realitzar fins i tot només de manera temporal i, per tant, no té les característiques d '"estabilitat" necessàries per modelar-ne una classe. Per contra, el concepte de lloc de treball està dotat de característiques d’estabilitat i persistència en el temps. En conseqüència, pensant des d’una perspectiva de programació orientada a objectes, es podrien construir una classe de persona i una classe de lloc de treball , que interactuessin entre si d’acord amb una relació del tipus de molts a molts amb l’esquema de “works-in”, on La persona exerceix el paper d’empleat, quan té una feina, i on, simètricament, la feina exerceix el paper del seu lloc de treball quan l’empleat hi treballa.

Tingueu en compte que amb aquest enfocament totes les classes es creen dins d'un únic "domini", en el sentit que descriuen entitats que es poden rastrejar fins a un únic àmbit pel que fa a la terminologia que les descriu, cosa que no és possible si s'utilitzen altres enfocaments. .

La diferència entre rols i classes és difícil d’entendre si adopteu construccions i funcions amb transparència referencial , és a dir, construccions i funcions que, quan reben el mateix paràmetre que l’entrada, sempre retornen el mateix valor, ja que els rols són tipus accessibles "per a referència ", mentre que les classes són tipus accessibles només quan s'instancia en objectes.

Programació orientada a components com a alternativa a l’herència

La programació orientada a components ofereix un mètode alternatiu per descriure i manipular l’esmentat sistema de persones, estudiants i empleats, per exemple definint un conjunt de classes auxiliars de Iscrizione i PostoDiLavoro per emmagatzemar la informació necessària per descriure l’estudiant i l’empleat respectivament. Per tant, es pot associar una col·lecció d'objectes del PostoDiLavoro de PostoDiLavoro a cada objecte Persona . Aquest procediment resol alguns dels problemes esmentats anteriorment:

  • una Persona ara pot tenir qualsevol nombre de llocs de treball i assistir a qualsevol nombre d’institucions educatives;
  • ara tots aquests treballs es poden canviar, afegir i suprimir dinàmicament;
  • ara és possible passar un objecte de Iscrizione com a paràmetre d'una funció (per exemple, una funció que ha de decidir si s'accepta una sol·licitud de registre) sense haver de passar com a paràmetres totes les dades que especifiquen dades personals (nom, edat, adreça) , etc.)

L’ús de components en lloc d’herència també produeix un codi escrit amb sintaxi menys ambigua i més fàcil d’interpretar. Compareu els dos exemples següents: el primer utilitza l’herència:

Impiegato i = getImpiegato();
print(i.mansioneDiLavoro());

És clar que la funció mansioneDiLavoro() es defineix a la classe Impiegato , però també es podria definir a la classe base Persona , cosa que podria provocar ambigüitats. Amb la programació de components, el programador pot reduir l'ambigüitat aplicant una jerarquia d'herències "més plana":

Persona p = getPersona();
print(p.impiego().mansione());

Sabent que la classe Impiego no té classes parentals, és evident que la mansione() es defineix a la classe Impiego

La programació orientada a components, però, pot no ser sempre una alternativa viable a la programació basada en l’herència, que, per exemple, permet el polimorfisme i l’ encapsulació . A més, la creació de classes de components també pot augmentar considerablement la longitud del codi a escriure.

Nota

  1. ^ a b Exemple a Java: la classe java.util.IdentityHashMap , pertanyent a les biblioteques estàndard del llenguatge, incompleix intencionadament el contracte general establert pel tipus java.util.Map , però, tal com es desprèn de la seva documentació, el fet que es vulnera el contracte general de la interfície del Map està ben documentat.

Articles relacionats

Control de l'autoritat GND ( DE ) 4277478-0
Informàtica Portal de TI : accediu a les entrades de Viquipèdia relacionades amb TI