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