La HERENCIA es un mecanismo que permite la creación de nuevas clases, basándonos en clases existentes. Esto facilita la reutilización de código y la organización de clases en jerarquías.
En este contexto “heredar” significa que las clases (o tipos) tienen disponibles algunas de las características de las que heredan. Como en el sentido biológico 👪.
Herencia: Este niño ¡ha heredado el mismo pelo que su madre!
En programación, la herencia es un mecanismo mediante el cuál unas clases pueden copiar atributos y métodos de otra clase.
Llámaremos,
- Clase hija o subclase: La clase que hereda
- Clase padre o super clase: La clase de la que se hereda
Por ejemplo, si tenemos una clase Animal
como clase padre, podríamos tener clases hijas como Perro
, Gato
, Ave
, Vaca
, etc.
Además de copiar los métodos de la clase padre, la clase hija puede agregar nuevos atributos o métodos, o incluso sobreescribir los que hereda (si no la herencia no tendría mucha gracia).
De esta forma, una clase hija puede extender y especializar el comportamiento de la clase padre. Copia parte del comportamiento, pero pone “sus propios detalles” y personalizaciones.
Cuándo usar la herencia
La forma más habitual de identificar que tenemos un caso de HERENCIA entre clases es detectar que comparten código común y queremos evitarnos copiarlo.
Por ejemplo, imaginemos que estamos trabajando en el sistema de gestión de un colegio. Tenemos clase Persona
class Persona
{
string Nombre;
string Apellidos;
// muchas más cosas
}
Ahora necesitamos las clases Alumno
, Profesor
, Tutor
, Director
. Pero todos tienen Nombre
y Apellidos
. Así que, en vez de copiarlo, podemos hacer que hereden.
class Alumno extends Persona
class Profesor extends Persona
class Tutor extends Persona
class Director extends Persona
Simplemente con eso, nuestras cuatro clases hijas Alumno
, Profesor
, Tutor
, Director
tienen disponible Nombre
y Apellidos
, porque las heredan de Persona
.
// tipo Alumno hereda de Persona
class Alumno
{
// 👇 esto lo tiene, sin necesidad de definirlo
// string Nombre;
// string Apellidos;
}
Efectivamente, evitarnos repetir código es uno de los propósitos principales de la herencia. Por tanto, es uno de los primeros indicadores de que es posible que tengamos un caso de herencia.
Pero, la cosa es mucho más interesante. El auténtico motivo para emplear herencia es identificar que tenemos una especificación de la clase padre (una especificación es un caso más concreto de un concepto).
Es decir Persona
es un concepto más amplio que Alumno
o Profesor
. Estos alumnos y profesores son casos particulares de personas, pero SON personas.
La herencia es una relación donde la clase hija es una versión más específica de la clase padre. Podéis detectarla si podéis construir una frase que contenga “es un/a”.
Por ejemplo:
- Un profesor es una persona
- Un pájaro es un animal
- Un coche es un vehículo
- Un zapato es un producto
Ejemplo de un Herencia
Vamos a ver cómo sería la sintaxis para hacer una herencia entre clases en diferentes lenguajes de programación.
Imaginamos que tienes que dibujar formas geométricas en un monitor de videojuegos 2D. Para eso tenemos una clase padre Shape
. De ella heredan Rectangle
, Circle
y Triangle
.
En C#, la herencia se logra utilizando el símbolo :
. Al heredar una clase de otra, la clase hija adquiere todos los campos y métodos de la clase padre
// clase padre
class Shape
{
public Point2D Position;
public int Rotation;
public int Width;
public int Height;
}
// clase hijas que heredan
class Rectangle : Shape { }
class Circle : Shape { }
class Triangle : Shape { }
En C++, la herencia se implementa utilizando la palabra clave public
seguida de :
. Esta relación de herencia pública permite que los miembros públicos de la clase base sean accesibles en la clase derivada.
// clase padre
class Shape
{
public:
Point2D Position;
int Rotation;
int Width;
int Height;
};
// clase hijas que heredan
class Rectangle : public Shape {};
class Circle : public Shape {};
class Triangle : public Shape {};
En JavaScript, la herencia se logra utilizando la palabra clave extends
, que permite que una clase hija herede de una clase padre.
// clase padre
class Shape {
constructor() {
this.Position = new Point2D();
this.Rotation = 0;
this.Width = 0;
this.Height = 0;
}
}
// clase hijas que heredan
class Rectangle extends Shape { }
class Circle extends Shape { }
class Triangle extends Shape { }
En Python, la herencia se logra colocando el nombre de la clase base entre paréntesis después del nombre de la clase hija. Esto indica que la clase hija hereda de la clase base.
Porque Python es así y hay que quererlo como es (supongo)
# clase padre
class Shape:
def __init__(self):
self.Position = Point2D()
self.Rotation = 0
self.Width = 0
self.Height = 0
# clase hijas que heredan
class Rectangle(Shape):
class Circle(Shape):
class Triangle(Shape):
En todos estos ejemplos, las clases hijas Rectangle
, Circle
y Triangle
disponen de todos los atributos y métodos de la clase padre Shape
porque heredan de ella.
¿De donde viene la herencia?
Por qué se inventó el concepto de HERENCIA, como parte fundamental de la Programación Orientada a Objetos. Volvemos una vez a lo básico, y recordemos que en esa época, ya era normal tener agrupaciones de variables que abstraían conceptos.
Sin embargo, cualquier persona podía venir y modificarte cualquier cosa. Básicamente podías tener un objeto Shape
, y a base de cambiarle formas hacer que sirviera para un rectángulo, un círculo o un triángulo.
Pero ya dijimos que eso daba lugar a líos inmantenibles. Motivo por el cuál la Programación Orientada a Objetos pone la ENCAPSULACION como uno de sus pilares. Con esto, nadie podía venir y modificarte un objeto.
Ahora habían “blindado” los OBJETOS, ya no podían modificarse cómo antes. Ahora necesariamente tenías que repetir el código para Rectangle
, Circle
y Triangle
, cada uno con todo el código entero. No era tampoco muy deseable.
Por ese motivo se inventó la HERENCIA. Para permitir un mecanismo que nos permitiera reaprovechar en código entre clases, de forma que no hubiera que copiarlo una y otra vez.
Pero, esta vez, haciéndolo de una forma regulada y controlada, correspondiendo con una relación de parentesco.