Bienvenidos una vez más a Ruby desde Cero. En el capítulo anterior estudiamos la primera parte del paradigma orientado a objetos aplicado al lenguaje.
En este nuevo capítulo veremos un poco más sobre el manejo de clases en Ruby, detallando para cada una de ellas sintaxis y ejemplos sencillos de fácil comprensión. Les recomiendo echar un vistazo a Ruby desde Cero: Orientación a Objetos – Parte 1, ya que, continuaremos con los desarrollando el contenido.
Herencia.
Es uno de los mecanismos mas utilizados en la programación orientada a objetos que nos facilitan la reutilización y la extensibilidad del código. La herencia es la relación entre una clase general y otra más específica. Por ejemplo: Si declaramos una clase Ave derivada de una clase Animal, todos los métodos y variables asociadas con la clase Animal, serán automáticamente heredados por la subclase Ave. Veamos el siguiente gráfico para comprender mejor esto:
Como vemos en el gráfico tenemos cinco componentes que heredan del componente ‘Animal’, estas subclases heredan todos los atributos y comportamientos del padre, incluso es posible alterarlos o agregarles nuevos atributos. El beneficio de la herencia es que las clases inferiores de la jerarquía obtienen las características de los de arriba, sino que también pueden añadir características específicas propias.
En Ruby, una clase sólo puede heredar de una sola clase padre. Algunos otros idiomas admiten la herencia múltiple, una característica que permite a las clases que hereden características de varias clases, pero Ruby no lo soporta.
La sintaxis para heredar de una clase es la siguiente:
class Objeto < Objeto_padre end
Para demostrar observemos el siguiente ejemplo:
class Animal attr_reader :habitat attr_accessor :descripcion attr_reader :color # variable publica de solo lectura def initialize # este metodo se llama automaticamente al instanciar el objeto end def respiracion puts "Inhala y exhala" end def color @color end def color=(color) @color = color end end class Reptil < Animal def initialize # este metodo se llama automaticamente al instanciar el objeto @habitat = "tierra" end end class Ave < Animal def initialize # este metodo se llama automaticamente al instanciar el objeto @habitat = "Aire" end end reptil = Reptil.new reptil.descripcion = "Lagarto" reptil.color = "verde" puts "El Animal es un: " + reptil.descripcion + ", color: " + reptil.color + ', habitat natural: ' +reptil.habitat reptil.respiracion ave = Ave.new ave.descripcion = "Buho" ave.color = "marron" puts "El Animal es un: " + ave.descripcion + ", color: " + ave.color + ', habitat natural: ' +ave.habitat ave.respiracion
En el ejemplo vemos como se componen tres clases (Animal (padre), Pez y Reptil (Subclases)). Vemos como Pez y Reptil heredan los atributos y comportamientos de la clase padre, por lo tanto desde el punto de vista del programador los Reptiles y Aves tienen una serie de atributos predefinidos (color, hábitat y descripción) y un comportamiento de respiración que simplemente inhala y exhala. Veamos el resultado de esto a continuación:
El Animal es un: Lagarto, color: verde, habitat natural: tierra Inhala y exhala El Animal es un: Buho, color: marron, habitat natural: Aire Inhala y exhala
Con el manejo de herencia surgen una serie de conceptos que hacen del paradigma orientado a objetos una técnica irresistible de usar. Estos conceptos los definimos a continuación:
Sustitución de métodos (Overriding)
La programación orientada a objetos existe una característica del lenguaje que permite una una implementación específica de un método que ya está proporcionado por una de sus superclases. Las modificaciones de las subclases reemplazan la implementación de la superclase.
A continuación mostraremos un ejemplo sencillo para entender mejor el concepto:
class Principal def metodo_prueba puts 'Es un metodo de la clase principal' end end class Subclase_uno < Principal def metodo_prueba puts 'Se sustituye el metodo principal' end end class Subclase_dos < Principal end principal = Principal.new principal.metodo_prueba puts "++++----++++" secundario = Subclase_uno.new secundario.metodo_prueba puts "++++----++++" tercero = Subclase_dos.new tercero.metodo_prueba
En este ejemplo se puede ver claramente como el objeto denominado ‘Subclase_uno’ remplaza el método ‘metodo_prueba’ del padre con otras características y como el objeto ‘subclase_dos’ simplemente hereda los métodos con el mismo comportamiento. Aquí el resultado de la ejecución del programa:
Es un metodo de la clase principal ++++----++++ Se sustituye el metodo principal ++++----++++ Es un metodo de la clase principal
Acceder a los métodos de la superclase
La programación orientada a objetos tiene una característica del lenguaje que permite que un método de una subclase poder tomar el comportamiento de la clase padre, para proporcionar características nuevas a las clases ya definidas en el método de la superclase. Las modificaciones de las subclases agregan nuevos comportamientos al método de la superclase.
Para demostrar esto veremos un ejemplo bastante sencillo, que seguramente nos ayudará a entender mejor este concepto:
class Principal def metodo_prueba puts 'Es un metodo de la clase principal' end end class Subclase_uno < Principal def metodo_prueba super() puts 'y se le agrega este nuevo comportamiento' end end principal = Principal.new principal.metodo_prueba puts "++++----++++" secundario = Subclase_uno.new secundario.metodo_prueba
En el ejemplo vemos como la subclase (‘Subclase_uno’) sustituye el método del padre (metodo_prueba) llamando a su implementación en la clase padre con la función ‘super()‘ para así agregarle nuevos comportamientos al método. El resultado al ejecutar este pequeño programa es el siguiente:
Es un metodo de la clase principal ++++----++++ Es un metodo de la clase principal y se le agrega este nuevo comportamiento
En conclusión la herencia permite crear una clase que es un perfeccionamiento o especialización de otra clase.
Sobrecarga de Métodos
En muchos lenguajes es posible crear dos versiones diferentes de un método con el mismo nombre. Sin embargo, una clase de Ruby sólo puede tener un método con un nombre (Si se definen dos métodos con el mismo nombre Ruby reconoce únicamente el ultimo definido). Una solución para este problema es crear un método único con una lógica para verificar cuantos y que tipos de argumentos hay. Demostremos esto con un ejemplo:
class Codehero def initialize(*args) if args.size > 1 puts 'No se permiten dos o mas atributos' else if args.size == 0 hola_mundo else hola(args[0]) end end end def hola_mundo puts "Hola CodeHero" end def hola(name) puts "Hola #{name}" end end ejemplo1 = Codehero.new('Ricardo Sampayo') ejemplo2 = Codehero.new ejemplo3 = Codehero.new('Ricardo','Sampayo')
En el ejemplo vemos como creamos un único constructor (‘initialize’) y en éste colocamos una lógica para manejar argumentos, que consiste en: si el constructor recibe más de un argumento imprime un mensaje de error, si no recibe argumentos llama al método ‘hola_mundo’ y si recibe un argumento llama al método ‘hola’ con la variable recibida. El resultado de esta ejecución es la siguiente:
Hola Ricardo Sampayo Hola CodeHero No se permiten dos o mas atributos
Conclusión
En este capítulo hemos adquirido herramientas importantes sobre el paradigma orientado a Objetos en Ruby, cómo el uso de herencias y sus conceptos más importantes y la sobrecarga de métodos.
Si te surgen dudas con nuestros cursos, no te detengas y danos tus comentarios, que con gusto estamos dispuestos a resolver tus inquietudes.
¡Hasta el próximo capítulo!