La herencia significa que se pueden crear nuevas clases partiendo de clases existentes, que tendrá todos los campos y los métodos de su 'superclase' o 'clase padre' y además se le podrán añadir otros campos y métodos propios.
Clase de la que desciende o deriva una clase. Las clases hijas (descendientes) heredan (incorporan) automáticamente las propiedades y métodos de la la clase padre.
Clase descendiente de otra. Hereda automáticamente los campos y métodos de su superclase. Es una especialización de otra clase.
Admiten la definición de nuevos campos y métodos para aumentar la especialización de la clase.
Veamos algunos ejemplos teóricos de herencia:
1) Imaginemos la clase Vehículo. Qué clases podrían derivar de ella?
Vehiculo Colectivo Moto Auto FordK Renault Fluence
Siempre hacia abajo en la jerarquía hay una especialización (las subclases añaden nuevos campos y métodos)
2) Imaginemos la clase Software. ¿Qué clases podrían derivar de ella?
Software DeAplicacion DeBase ProcesadorTexto PlanillaDeCalculo SistemaOperativo Word WordPerfect Excel Lotus123 Linux Windows
Si vemos que dos clases responden a la pregunta ClaseA "..es un.." ClaseB es posible que haya una relación de herencia.
Por ejemplo:
Auto "es un" Vehiculo Circulo "es una" Figura Mouse "es un" DispositivoEntrada Suma "es una" Operacion
Plantear una clase Persona que contenga dos campos: nombre y edad. Definir como responsabilidades la carga, impresión y mostrar si es mayor de edad.
En el bloque principal del programa definir un objeto de la clase Persona y llamar a sus métodos.
Declarar una segunda clase llamada Empleado que herede de la clase Persona y agregue un campo sueldo y muestre si debe pagar impuestos (sueldo superior a 3000)
También en la clase empleado permitir cargar los datos e imprimir.
En el bloque principal también definir un objeto de la clase Empleado y llamar a sus métodos.
program Proyecto132; {$APPTYPE CONSOLE} type TPersona = class Nombre: String; Edad: Integer; procedure Cargar; procedure Imprimir; procedure EsMayorDeEdad; end; TEmpleado = class(TPersona) Sueldo: Double; procedure Cargar; procedure Imprimir; procedure PagaImpuestos; end; procedure TPersona.Cargar; begin Write('Ingrese el nombre:'); ReadLn(Nombre); Write('Ingrese la edad:'); ReadLn(Edad); end; procedure TPersona.Imprimir; begin WriteLn('Nombre:', Nombre); WriteLn('Edad:', Edad); end; procedure TPersona.EsMayorDeEdad; begin if Edad >= 18 then WriteLn('Es mayor de edad') else WriteLn('No es mayor de edad'); end; procedure TEmpleado.Cargar; begin inherited Cargar; Write('Ingrese el sueldo:'); ReadLn(Sueldo); end; procedure TEmpleado.Imprimir; begin inherited Imprimir; WriteLn('Sueldo:', Sueldo:0:2) end; procedure TEmpleado.PagaImpuestos; begin if Sueldo > 3000 then WriteLn('El empleado ', Nombre, ' debe pagar impuestos') else WriteLn('El empleado ', Nombre, ' no debe pagar impuestos') end; var Persona1: TPersona; Empleado1: TEmpleado; begin Persona1 := TPersona.Create; WriteLn('Datos de la persona'); Persona1.Cargar; Persona1.Imprimir; Persona1.EsMayorDeEdad; Persona1.Free; Empleado1 := TEmpleado.Create; WriteLn('Datos del empleado'); Empleado1.Cargar; Empleado1.Imprimir; Empleado1.EsMayorDeEdad; Empleado1.PagaImpuestos; Empleado1.Free; ReadLn; end.
La declaración TPersona no sufre cambios con respecto al ejercicio que desarrollamos conceptos anteriores:
TPersona = class Nombre: String; Edad: Integer; procedure Cargar; procedure Imprimir; procedure EsMayorDeEdad; end;
Lo mismo la implementación de los métodos de la clase TPersona no presentan cambios:
procedure TPersona.Cargar; begin Write('Ingrese el nombre:'); ReadLn(Nombre); Write('Ingrese la edad:'); ReadLn(Edad); end; procedure TPersona.Imprimir; begin WriteLn('Nombre:', Nombre); WriteLn('Edad:', Edad); end; procedure TPersona.EsMayorDeEdad; begin if Edad >= 18 then WriteLn('Es mayor de edad') else WriteLn('No es mayor de edad'); end;
Lo nuevo se presenta cuando declaramos la clase TEmpleado indicando que la misma hereda de la clase TPersona:
TEmpleado = class(TPersona)
Entre paréntesis luego de la palabra clave class indicamos el nombre de la clase que heredamos.
Es importante decir que la declaración de la clase TPersona debe estar obligatoriamente antes de la declaración de la clase TEmpleado, sino se produce un error sintáctico.
Con indicar que la clase TEmpleado hereda de TPersona significa que la clase TEmpleado ya tiene dos campos (Nombre y Edad) y tres métodos (Cargar, Imprimir y EsMayorDeEdad)
La clase TEmpleado define el campo Sueldo y tres métodos:
Sueldo: Double; procedure Cargar; procedure Imprimir; procedure PagaImpuestos;
El método Cargar tendrá por objetivo cargar los datos del empleado (Nombre, Edad y Sueldo):
procedure TEmpleado.Cargar; begin inherited Cargar; Write('Ingrese el sueldo:'); ReadLn(Sueldo); end;
Dentro del método cargar y con las ventajas que nos proporciona la herencia, mediante la palabra clave inherided llamamos al método Cargar de la clase padre (donde se carga el nombre y la edad) y luego solicitamos el ingreso del Sueldo.
La herencia nos permite reducir línea de código evitando tener que escribir:
procedure TEmpleado.Cargar; begin Write('Ingrese el nombre:'); ReadLn(Nombre); Write('Ingrese la edad:'); ReadLn(Edad); Write('Ingrese el sueldo:'); ReadLn(Sueldo); end;
De forma similar cuando codificamos el método Imprimir de la clase TEmpleado llamamos al método Imprimir de la clase padre:
procedure TEmpleado.Imprimir; begin inherited Imprimir; WriteLn('Sueldo:', Sueldo:0:2) end;
El tercer método tiene por objetivo mostrar si paga impuestos el empleado:
procedure TEmpleado.PagaImpuestos; begin if Sueldo > 3000 then WriteLn('El empleado ', Nombre, ' debe pagar impuestos') else WriteLn('El empleado ', Nombre, ' no debe pagar impuestos') end;
Es importante notar que podemos acceder al campo Nombre, que si bien no está definido en la clase TEmpleado si lo está en la clase TPersona.
Definimos dos variables globales, uno de tipo TPersona y otra de tipo TEmpleado:
var Persona1: TPersona; Empleado1: TEmpleado;
Creamos el objeto Persona1 y llamamos a sus métodos:
begin Persona1 := TPersona.Create; WriteLn('Datos de la persona'); Persona1.Cargar; Persona1.Imprimir; Persona1.EsMayorDeEdad; Persona1.Free;
Luego creamos el objeto Empleado1 y también llamamos a sus métodos:
Empleado1 := TEmpleado.Create; WriteLn('Datos del empleado'); Empleado1.Cargar; Empleado1.Imprimir; Empleado1.EsMayorDeEdad; Empleado1.PagaImpuestos; Empleado1.Free; ReadLn; end.
Es importante notar que podemos llamar al método EsMayorDeEdad desde el objeto Empleado1 gracias a que este objeto hereda de la clase TPersona.
Ahora ya podemos decir de donde salen los métodos Create y Free, son métodos heredados de la clase TObject.
Toda clase hereda directamente o indirectamente de la clase TObject. Podemos modificar el programa y utilizar la sintaxis:
TPersona = class(TObject)
Es decir que toda clase si no indicamos que hereda de otra luego está heredando de la clase TObject que define los métodos Create y Free.
Gracias a esta herencia podemos escribir:
Persona1 := TPersona.Create; ... Persona1.Free;
Lo mismo sucede con la clase TEmpleado al heredar de la clase TPersona y esta heredar de la clase TObject podemos codificar:
Empleado1 := TEmpleado.Create; ... Empleado1.Free;
Declarar una clase llamada TCalculadora que disponga de dos métodos para la carga de valores de tipo Double (almacenar los datos en dos campos)
Definir las responsabilidades de sumar, restar, multiplicar, dividir e imprimir.
Declarar luego una clase llamada TCalculadoraCientifica que herede de TCalculadora y añada las responsabilidades de calcular el cuadrado del primer número y la raíz cuadrada.
program Proyecto133; {$APPTYPE CONSOLE} type TCalculadora = class Valor1: Double; Valor2: Double; Resultado: Double; procedure Cargar1; procedure Cargar2; procedure Sumar; procedure Restar; procedure Multiplicar; procedure Dividir; procedure Imprimir; end; TCalculadoraCientifica = class(TCalculadora) procedure Cuadrado; procedure Raiz; end; procedure TCalculadora.Cargar1; begin Write('Ingrese valor:'); ReadLn(Valor1); end; procedure TCalculadora.Cargar2; begin Write('Ingrese valor:'); ReadLn(Valor2); end; procedure TCalculadora.Sumar; begin Resultado := Valor1 + Valor2; end; procedure TCalculadora.Restar; begin Resultado := Valor1 - Valor2 end; procedure TCalculadora.Multiplicar; begin Resultado := Valor1 * Valor2; end; procedure TCalculadora.Dividir; begin Resultado := Valor1 / Valor2; end; procedure TCalculadora.Imprimir; begin WriteLn('Resultado:', Resultado:0:2); end; procedure TCalculadoraCientifica.Cuadrado; begin Resultado := Valor1 * Valor1; end; procedure TCalculadoraCientifica.Raiz; begin Resultado := Sqrt(Valor1); end; var Calculadora1: TCalculadora; CalculadoraCientifica1: TCalculadoraCientifica; begin WriteLn('Uso de la calculadora'); Calculadora1 := TCalculadora.Create; Calculadora1.Cargar1; Calculadora1.Cargar2; Calculadora1.Sumar; Calculadora1.Imprimir; Calculadora1.Restar; Calculadora1.Imprimir; Calculadora1.Multiplicar; Calculadora1.Imprimir; Calculadora1.Dividir; Calculadora1.Imprimir; Calculadora1.Free; WriteLn('Uso de la calculadora científica'); CalculadoraCientifica1 := TCalculadoraCientifica.Create; CalculadoraCientifica1.Cargar1; CalculadoraCientifica1.Cargar2; CalculadoraCientifica1.Sumar; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Restar; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Multiplicar; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Dividir; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Cuadrado; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Raiz; CalculadoraCientifica1.Imprimir; CalculadoraCientifica1.Free; ReadLn; end.
La clase TCalculadora hereda por defecto de la clase TObject, que por lo general no se escribe:
TCalculadora = class Valor1: Double; Valor2: Double; Resultado: Double; procedure Cargar1; procedure Cargar2; procedure Sumar; procedure Restar; procedure Multiplicar; procedure Dividir; procedure Imprimir; end;
Definimos 3 campos y 7 métodos, más abajo codificamos los 7 métodos.
La clase TCalculadoraCientifica hereda de la clase TCalculadora y añade dos responsabilidades que permita calcular el cuadrado y la raiz cuadrada de un número:
TCalculadoraCientifica = class(TCalculadora) procedure Cuadrado; procedure Raiz; end;
Tengamos en cuenta que con la herencia la clase TCalculadoraCientifica es una calculadora común, es decir que podemos sumar, restar, multiplicar y dividir, pero añade los métodos de cálculo del cuadrado y la raíz.
Valor := Random(6) + 1;Imprimir el valor del dado.
program Proyecto134; {$APPTYPE CONSOLE} type TDado = class Valor: Integer; procedure Tirar; procedure Imprimir; end; TDadoRecuadro = class(TDado) procedure Imprimir; end; procedure TDado.Tirar; begin Valor := Random(6) + 1; end; procedure TDado.Imprimir; begin WriteLn(Valor); end; procedure TDadoRecuadro.Imprimir; begin WriteLn('***'); WriteLn('*', Valor, '*'); WriteLn('***'); end; var Dado1: TDado; DadoRecuadro1: TDadoRecuadro; begin Randomize; //Generamos una semilla de valores aleatorios distinta para cada ejecución del programa WriteLn('Prueba del dado común'); Dado1 := TDado.Create; Dado1.Tirar; Dado1.Imprimir; Dado1.Free; WriteLn('Prueba del dado con recuadro'); DadoRecuadro1 := TDadoRecuadro.Create; DadoRecuadro1.Tirar; DadoRecuadro1.Imprimir; DadoRecuadro1.Free; ReadLn; end.