Herència (programació)

De Viquipèdia
Dreceres ràpides: navegació, cerca
Herència representada amb UML.[1] La classe derivada tindrà els atributs x i y, i els mètodes a() i b().

L’Herència és un dels conceptes fonamentals de la programació orientació a objectes i té gran importància en el desenvolupament de software. S’utilitza l’herència per crear classes noves a partir de les ja definides. Una classe nova pot ser una extensió o bé una restricció de la classe original. A més d’aquest aspecte estructural, serveix també per documentar les semblances entre les classes, que és particularment important durant les primeres etapes del disseny de software, la nova classe està unida amb la classe original amb una relació "es-un" (engl. Is-A).

La classe que hereta es diu classe base (també super-classe o classe pare), la classe heretada s’anomena classe derivada (també sub-classe o classe fill), aquesta pren o hereta els atributs (variables) i comportament (mètodes) de la classe base (reutilitza codi). Aquest procés es denomina especialització, en sentit contrari en diríem generalització, termes limitats principalment a nivell de models. En llenguatge UML (Unified Modeling Language) una relació d'herència es representa amb una fletxa triangular, apuntant a la classe base des de la classe derivada, els mètodes i atributs heretats no es repeteixen en la representació de la sub-classe.

El 1967 el llenguatge de programació Simula introduïa per primera vegada el concepte d'herència juntament amb altres conceptes de la programació orientada a objectes, com ara el polimorfisme.[2]

Les classes permeten especificar els tipus de les dades i les funcions que seran heretades. Alguns llenguatges de programació separen aquests aspectes encara que sigui parcialment, i distingeixen entre classe i interfície (engl. Interface).

Accés, visibilitat i ocultació[modifica | modifica el codi]

Superclasse

Una superclasse, classe base o classe pare

En alguns llenguatges de programació, el dissenyador pot definir quines variables d'instància i mètodes dels objectes són visibles. En Java i C++ això s'aconsegueix amb les especificacions private, protected i public. Només les variables definides com a públic seran visibles per tots els objectes. En algun altre llenguatge com a Smalltalk, totes les variables són privades, i tots els mètodes publics

  • Private: Els elements privats de la superclasse no són visibles ni en la subclasse ni en ningù més
  • Protected: Els elements de la superclasse només són visibles en la subclasse
  • Public: Visibles per a tothom
public class Vehicle {

    //Atributs amb accés protected
    protected String model;
    protected int rodes;
    protected int velocitat;
    
    //Atribut privat
    private String codi_vehicle;

    //El constructor ha de ser sempre pùblic
    public Vehicle(String model, int rodes, int velocidat) {
        this.model = model;
        this.rodes = rodes;
        this.velocitat = velocitat;
    }   
    public String getModel() {
        return model;
    }
    public int getRodes(){
        return rodes;
    }
    public int getVelocitat() {
        return velocitat;
    }
}

Exemple d'herència[modifica | modifica el codi]

Exemple d'aplicació de l'herència ambUML.

El següent exemple representa una possible aplicació de l’herència, en aquest cas es tracta d’un sistema de lloguer de cotxes.

La classe base conté els atributs bàsics d’un vehicle qualsevol un identificador, el seu pes i el color, a més d’un mètode per comprovar la disponibilitat. Dintre del sistema distingirem entre vehicles amb motor o bicicletes, les especialitzacions heretaran els atributs i mètodes comuns, i n’afegiran els propis.

Cada objecte de la classe VehicleAmbMotor tindrà com atributs a part de l’identificador, pes i color, el quilometratge i la velocitat màxima del vehicle, i un mètode per comprovar la validesa del permís de conduir. Dins la classe Bicicleta l’unica informació addicional que podem necessitar en aquest cas és el tipus (muntanya, carretera, etc).

De la classe VehicleAmbMotor derivaran tres classes: Moto, Cotxe i Camió. La classe Cotxe afegeix informació sobre el nombre de places i la classe Camió sobre la càrrega que pot portar. Podem veure que totes tres classes hauran de sobreescriure el mètode comprovarPermis(x), ja que es necessiten permisos de conduir diferents per portar un vehicle o un altre.


Exemple d'implementació de l'herència[modifica | modifica el codi]

La implantació és heretada de la classe base i implícitament també la seva interfície. La classe derivada assumeix el control dels atributs i funcions de la classe base, si és necessari afegeix atributs o funcions depenent si són rellevants o no per aquesta especialització.

L’exemple següent està escrit amb llenguatge Java, descriu la derivació d’un quadrat com una especialització del rectangle.

La classe rectangle té com atributs l’amplada i la longitud, que es passen com a paràmetres en el constructor. A més es defineixen mètodes per al càlcul del perímetre i la diagonal del rectangle (a més dels mètodes consultors).

L'especialització de quadrat, estén la funcionalitat de rectangle. El constructor de quadrat accepta només un paràmetre, la longitud del costat, dintre crida al constructor de la classe base passant-li com a amplada i longitud el mateix valor, i per últim sobreescriu el comportament del mètode getDiagonal(), d’aquesta manera quan l’executem sobre un objecte de tipus quadrat s’executarà aquest codi i no el de la classe base.

public class Rectangle
{
    private double longitud;
    private double amplada;
 
    public Rectangle(double longitud, double amplada)
    {
        this.amplada = amplada;
        this.longitud = longitud;
    }
 
    public double getLongitud() { return longitud; }
    public double getAmplada() { return amplada; }
 
    public double getPerimetre()
    {
        return 2 * longitud + 2 * amplada;
    }
 
    public double getDiagonal()
    {
        return Math.sqrt(longitud * longitud + amplada * amplada);
    }
}
public class Quadrat extends Rectangle
{
    // càlcul de l'arrel quadrada
    static final double arrel2 = Math.sqrt(2);
 
    public Quadrat(int longitud)
    {
        // Crida al constructor de la classe pare
        super(longitud, longitud);
    }
 
    @Override
    public double getDiagonal()
    {
        return arrel2 * getlongitud();
    }
}

Usos de l'herència [3][modifica | modifica el codi]

L'herència serveix per relacionar una o més classes entre elles.

Herència d'interfície:

Molts llenguatges de programació orientats a objectes permeten a una classe o objecte tornar a implementar el comportament dels mètodes que li ve de la classe base. Aquí s'origina una complicació: Quin comportament cal agafar, el de la classe base o la classe derivada? Això depèn del llenguatge que fem servir, ja que alguns permeten indicar que no està sobreescrit i per tant, cal usar el de la classe base. Una alternativa a aquesta pràctica seria amagar el codi heretat.

Herència d'implementació:

L’herència per implementació és un mecanisme pel qual una classe derivada reutlitza el codi de la classe base. Per defecte, totes les operacions de la classe base passa a la classe derivada, però aquesta pot reescriure la implementació d’alguns o tots els mètodes.

L’herència de classe només per reutilitzar codi ha deixat de fer-se servir. La principal preocupació és que no assegura la substituibilitat polimorfica.(una instància de la classe reusada no necessàriament és substituïda per una instància de la classe heretada.

Una tècnica alternativa per evitar això és la delegació però és més difícil d’implementar.

Herència vs subtipatge[modifica | modifica el codi]

L'herència i el subtipatge tenen coses similars i alhora diferències. El subtipatge permet reemplaçar un tipus de dades per un altre tipus o una abstracció. A més, es diu que s'estableix una relació de és un entre el subtipus i alguna abstracció existent, ja sigui implícita o explicita, depenent del suport del llenguatge amb el qual s'està treballant.

Aquesta relació pot ser expressada mitjançant l'herència en llenguatges que suporten l'herència com una forma de subtipatge. En l'exemple següent farem àguila i colom subtipus d'ocell.

abstract class Ocell{
   abstract String parla();
}

class Aguila extends Ocell{
   String parla(){
      return "L'aguila crida o xiscla";
   }
}

class Colom extends Ocell{
   String parla(){
      return "El colom marruqueja";
   }
}

void escoltem(Ocell o){
   println(o.parla());
}

void main(){
   escoltem(new Aguila());
   escoltem(new Colom());
}

Restriccions de disseny[modifica | modifica el codi]

L'ús extensiu de l'herència mentre es dissenya un programa imposa certes restriccions.

Posarem el següent exemple per tal d'explicar algunes restriccions:

Tenim una classe Persona on hi guardem el nom, la data de naixement, l'adreça i el número de telèfon. Definim una subclasse de Persona anomenada Estudiant on guardem la seva nota mitjana i les assignatures que cursa. També definim una altra subclasse de Persona anomenada Empleat on guardem el títol de la persona, la feina i el salari.

Amb aquest exemple tenim diferents restriccions:

Herència d'una sola classe[modifica | modifica el codi]

Fent servir l'herència simple, una classe derivada només pot heretar d'una classe base Seguint amb l'exemple de la Persona, una persona pot ser estudiant o empleat, però no els dos. Podríem definir una classe que fos EstudiantEmpleat i que les seves classes bases fossin Empleat i Estudiant. Tot i així, no solucionaríem el problema del tot, ja que no podria treballar a dos llocs diferents ni estudiar a dos institucions diferents.

Estàtic[modifica | modifica el codi]

Un objecte no pot canviar de tipus amb el temps, de manera que(seguint l'exemple anterior), no seria possible que un Estudiant es transformés en un Empleat mentre segueix conservant l'estat de la seva classe base(Persona en aquest cas).

Tot i així, aquest comportament es pot adquirir mitjançant el decorator.

Visibilitat[modifica | modifica el codi]

Quan es té accés a un objecte, es té accés a totes les dades de l'objecte i les de la classe base. En l'exemple anterior, és impossible obtenir un punter a la mitjana de l'estudiant sense tenir accés a les dades de la classe base. Hi ha alguns llenguatges que utilitzant l'atribut protected ens permet protegir aquests atributs de manera que només els objectes dins de l'herència poden tenir-hi accés.

Tipus d'herència[modifica | modifica el codi]

Herència simple[modifica | modifica el codi]

La classe derivada hereta d'una única classe base. En conseqüència només pot heretar d'una sola classe base i de cap altre.

Herència múltiple[modifica | modifica el codi]

La classe derivada pot heretar de més d’una classe base, és a dir, pot tenir varis "pares". En aquest aspecte existeixen discrepàncies entre els dissenyadors de llenguatges, alguns dels quals prefereixen no admetre l'herència múltiple degut als grans conflictes que crea aquesta mateixa en mètodes i variables amb el mateix nom. És per tant que la majoria dels llenguatges orientats a objectes permeten herència simple, en contrast de menys llenguatges que permeten l'ùs de l'herència múltiple, entre ells: C++, Eiffel, Python, mentre que Java, Ada, C# i Smalltalk sols permeten herència simple.

Herència Multinivell[modifica | modifica el codi]

Una subclasse hereta d'una altra subclasse la qual aquesta hereta de la classe base. Així apareix el concepte de "nivell". La classe A serveix com a classe base per la classe derivada B, que a la vegada serveix com de classe base per la classe derivada C, i així successivament. La classe B es coneix com a classe intermèdia, ja que proporciona un "enllaç" per l'herència entre la classe A i C. La cadena ABC és coneguda com la ruta de l'herència. Aquest procés es pot utilitzar amb diversos nivells.

Herència Jeràrquica[modifica | modifica el codi]

La classe base és superclasse de més d'una subclasse.

Herència Híbrida[modifica | modifica el codi]

És una barreja de dos o més dels tipus anteriors d'herència. A tall d'exemple pot existir una herència simple i a la vegada jeràrquica.

Jerarquització[4][modifica | modifica el codi]

En aquest procés es creen organitzacions d'elements en diferents nivells. No és només un concepta de la Programació Orientada a a Objectes, sinó que és quelcom que observem en la vida real en molts àmbils. Es poden tenir diversos tipus de jerarquíes, com la classificació o la composició.

Composició[modifica | modifica el codi]

Quan es pot dir que uns elements estan composts d'altres, o que uns elements estan presents en altres. Per exemple, el sistema respiratori, els pulmons, el nas... Podem dir que els pulmons están "dins" el sistema respiratori, així com dins els pulmons hi trobem bronquis i alveols. En aquesta jerarquía hi tenim composició, ja que uns formen part d'altres.

Classificació[modifica | modifica el codi]

Es produeix quan uns elements són una especialització d'altres. Per exemple els animals, tenim vertebrats i invertebrats, i dins els vertebrats hi trobem mamífers, aus, rèptils... En la família dels mamífers gossos, gats, conills...

Vegeu també[modifica | modifica el codi]

Referències[modifica | modifica el codi]

  1. «Tutorial UML» (en castellà).
  2. Ole-Johan Dahl, Kristen Nygaard: Class and Subclass Declarations. In: J. N. Buxton (Hrsg.): Simulation Programming Languages. Proceedings of the IFIP working conference on simulation programming languages, Oslo, Mai 1967 North-Holland, Amsterdam, 1968, Seite 158–174 (online; PDF; 693 kB)
  3. «POO Herència».
  4. «Herencia en Programación Orientada a Objetos».