Vimos en el concepto anterior que dos clases pueden estar relacionadas por la colaboración. Ahora veremos otro tipo de relación entre clases que es la Herencia.
La herencia significa que se pueden crear nuevas clases partiendo de clases existentes, que tendrá todas los atributos y los métodos de su 'superclase' o 'clase padre' y además se le podrán añadir otros atributos y métodos propios.
Clase de la que desciende o deriva una clase. Las clases hijas (descendientes) heredan (incorporan) automáticamente los atributos y métodos de la la clase padre.
Clase descendiente de otra. Hereda automáticamente los atributos y métodos de su superclase. Es una especialización de otra clase.
Admiten la definición de nuevos atributos 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 atributos 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
El primer tipo de relación que habíamos visto entre dos clases, es la de colaboración. Recordemos que es cuando una clase contiene un objeto de otra clase como atributo.
Cuando la relación entre dos clases es del tipo "...tiene un..." o "...es parte de...", no debemos implementar herencia. Estamos frente a una relación de colaboración de clases no de herencia.
Si tenemos una ClaseA y otra ClaseB y notamos que entre ellas existe una relacion de tipo "... tiene un...", no debe implementarse herencia sino declarar en la clase ClaseA un atributo de la clase ClaseB.
Por ejemplo: tenemos una clase Auto, una clase Rueda y una clase Volante. Vemos que la relación entre ellas es: Auto "...tiene 4..." Rueda, Volante "...es parte de..." Auto; pero la clase Auto no debe derivar de Rueda ni Volante de Auto porque la relación no es de tipo-subtipo sino de colaboración. Debemos declarar en la clase Auto 4 atributos de tipo Rueda y 1 de tipo Volante.
Luego si vemos que dos clase 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 atributos: nombre y edad. Definir como responsabilidades la carga por teclado y su impresión.
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 atributo sueldo y muestre si debe pagar impuestos (sueldo superior a 3000)
También en el bloque principal del programa crear un objeto de la clase Empleado.
class Persona def initialize print "Ingrese el nombre:" @nombre = gets.chomp print "Ingrese la edad:" @edad = gets.to_i end def imprimir puts "Nombre: #{@nombre}" puts "Edad: #{@edad}" end end class Empleado < Persona def initialize super print "Ingrese el sueldo:" @sueldo = gets.to_f end def imprimir super puts "Sueldo: #{@sueldo}" end def paga_impuestos if @sueldo > 3000 puts "El empleado #{@nombre} debe pagar impuestos" else puts "No paga impuestos" end end end # bloque principal persona1 = Persona.new persona1.imprimir puts "*" * 50 empleado1 = Empleado.new empleado1.imprimir empleado1.paga_impuestos
La clase Persona no tiene ninguna sintaxis nueva, como vemos definimos sus métodos initialize e imprimir, y sus dos atributos @nombre y @edad:
class Persona def initialize print "Ingrese el nombre:" @nombre = gets.chomp print "Ingrese la edad:" @edad = gets.to_i end def imprimir puts "Nombre: #{@nombre}" puts "Edad: #{@edad}" end end
En el bloque principal creamos un objeto de la clase Persona:
# bloque principal persona1 = Persona.new persona1.imprimir
La herencia se presenta en la clase Empleado, en la declaración de la clase indicamos luego del caracter < el nombre de la clase de la cual hereda:
class Empleado < Persona
Con esta sintaxis indicamos que la clase Empleado hereda de la clase Persona. Luego de esto podemos decir que la clase Empleado ya tiene dos atributos heredados que son el @nombre y la @edad, también hereda las funcionalidades initialize e imprimir.
La clase Empleado define un nuevo atributo que es el @sueldo y tres funcionalidades initialize, imprimir y paga_impuestos.
En el método initialize de la clase Empleado primero llamamos al método initialize de la clase padre y luego cargamos el sueldo:
def initialize super print "Ingrese el sueldo:" @sueldo = gets.to_f end
Mediante la palabra clave super podemos llamar al método initialize de la clase padre.
Lo mismo sucede con el método imprimir donde primero llamamos al imprimir de la clase padre y luego mostramos el sueldo del empleado:
def imprimir super puts "Sueldo: #{@sueldo}" end
La tercer funcionalidad es (como vemos tenemos acceso al atributo @nombre por la herencia):
def paga_impuestos if @sueldo > 3000 puts "El empleado #{@nombre} debe pagar impuestos" else puts "No paga impuestos" end end
En el bloque principal de nuestro programa definimos un objeto de la clase Empleado y llamamos a sus métodos:
empleado1 = Empleado.new empleado1.imprimir empleado1.paga_impuestos
Ahora plantearemos otro problema empleando herencia. Supongamos que necesitamos implementar dos clases que llamaremos Suma y Resta. Cada clase tiene como atributo valor1, valor2 y resultado. Los métodos a definir son cargar1 (que inicializa el atributo valor1), carga2 (que inicializa el atributo valor2), operar (que en el caso de la clase "Suma" suma los dos atributos y en el caso de la clase "Resta" hace la diferencia entre valor1 y valor2)
Si analizamos ambas clases encontramos que muchos atributos y métodos son idénticos. En estos casos es bueno definir una clase padre que agrupe dichos atributos y responsabilidades comunes.
La relación de herencia que podemos disponer para este problema es:
Operacion Suma Resta
Solamente el método operar es distinto para las clases Suma y Resta (esto hace que no lo podamos disponer en la clase Operacion en principio), luego los métodos cargar1 y cargar2 son idénticos a las dos clases, esto hace que podamos disponerlos en la clase Operacion. Lo mismo los atributos valor1, valor2 y resultado se definirán en la clase padre Operacion.
class Operacion attr_reader :resultado def initialize @valor1 = 0 @valor2 = 0 @resultado = 0 end def cargar1 print "Ingrese primer valor:" @valor1 = gets.to_i end def cargar2 print "Ingrese segundo valor:" @valor2 = gets.to_i end end class Suma < Operacion def operar @resultado = @valor1 + @valor2 end end class Resta < Operacion def operar @resultado = @valor1 -@valor2 end end # bloque princpipal suma1 = Suma.new suma1.cargar1 suma1.cargar2 suma1.operar puts "La suma de los dos valores es #{suma1.resultado}" resta1 = Resta.new resta1.cargar1 resta1.cargar2 resta1.operar puts "La resta de los valores es:#{resta1.resultado}"
En este problema la clase Operación tiene por objetivo agrupar atributos y funcionalidades que se heredarán en otras clases.
La clase Operación asigna un cero a los tres atributos en el metodo initialize:
class Operacion attr_reader :resultado def initialize @valor1 = 0 @valor2 = 0 @resultado = 0 end
Define dos métodos para cargar los atributos valor1 y valor2:
def cargar1 print "Ingrese primer valor:" @valor1 = gets.to_i end def cargar2 print "Ingrese segundo valor:" @valor2 = gets.to_i end
Definimos el atributo @resultado mediante attr_reader para tener acceso a su valor desde fuera de la clase:
attr_reader :resultado
En el bloque principal de nuestro programa no creamos objetos de la clase Operación. La clase Operación tiene sentido que otras clases hereden de esta.
Tanto la clase Suma y Resta heredan de la clase Operación y definen el método operar con la funcionalidad que le corresponde a cada clase:
class Suma < Operacion def operar @resultado = @valor1 + @valor2 end end class Resta < Operacion def operar @resultado = @valor1 -@valor2 end end
Finalmente en el bloque principal de nuestro programa creamos un objeto de la clase Suma y otro de la clase Resta y llamamos a sus respectivos métodos en un orden lógico:
# bloque princpipal suma1 = Suma.new suma1.cargar1 suma1.cargar2 suma1.operar puts "La suma de los dos valores es #{suma1.resultado}" resta1 = Resta.new resta1.cargar1 resta1.cargar2 resta1.operar puts "La resta de los valores es:#{resta1.resultado}"
Podemos acceder al atributo @resultado gracias que definimos el attr_reader respectivo.
ejercicio149.rb class Cuenta def initialize(titular, monto) @titular = titular @monto = monto end def imprimir puts "Titular: #{@titular}" puts "Monto: #{@monto}" end end class CajaAhorro < Cuenta def initialize(titular, monto) super titular, monto end def imprimir puts "Cuenta de caja de ahorro" super end end class PlazoFijo < Cuenta def initialize(titular, monto, plazo, interes) super titular, monto @plazo = plazo @interes = interes end def imprimir puts "Cuenta de plazo fijo" super puts "Plazo en dias: #{@plazo}" puts "Interes: #{@interes}" ganancia_interes end def ganancia_interes ganancia = @monto * @interes / 100 puts "Importe del interes: #{ganancia}" end end # bloque principal cajaahorro = CajaAhorro.new "Juan", 2000 cajaahorro.imprimir plazofijo = PlazoFijo.new "Diego", 10000, 30, 0.75 plazofijo.imprimir