POO III – Herencia y Polimorfismo

En el post anterior, vimos lo que es encapsulamiento, pero al final del artículo quedó un pequeño mal sabor de boca y es que, si volvemos y vemos las clases Dog y Cat son exactamente iguales, y eso por supuesto, no es una buena práctica de programación. Veamos qué nos dice el principio DRY (don’t repeat your self o No te repitas en español):

Según este principio toda pieza de información (código) nunca debería ser duplicada debido a que la duplicación incrementa la dificultad en los cambios y evolución posterior, puede perjudicar la claridad y crear un espacio para posibles inconsistencias.

Vemos claramente que violamos este tan elemental principio. Entonces, ¿qué podemos hacer para erradicar este mal hábito? Esto lo hacemos mediante una técnica de la programación orientada a objetos llamada Herencia.

¿Qué es la herencia?

La herencia es una característica de la programación orientada a objetos, la cual nos provee una manera de reutilizar código en lugar de volver a escribirlo (DRY). La herencia, como su nombre lo indica, hereda los atributos y métodos de una clase (superclase) , de tal modo que estarán disponibles para todas las subclases de forma implícita.

Podemos reestructurar nuestras clases Dog y Cat para que hereden de una clase llamada Animal:

abstract class Animal {
    private String name;
    private String race;
    private String color;
    private float weight;

    public Animal() {

    }

    public Animal(String name, String race, String color, float weight) {
        this.name = name;
        this.race = race;
        this.color = color;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRace() {
        return race;
    }

    public void setRace(String race) {
        this.race = race;
   }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public float getWeight() {
        return weight;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }

    public void eat() {
        System.out.println("Estoy comiendo, jugamos más tarde");
    }

    public void talk() {
        System.out.println("Guarf! guarf!");
    }

    public void run() {
        System.out.println("Abran pasoooo");
    }

    public void sleep() {
        System.out.println("zZzZzZzZzZzZzZzZ");
    }

}

Presta atención a la clase Animal, esta tiene un modificador nuevo: abstract. Una clase marcada como abstract es una clase que no puede ser instanciada, la cual, solo nos servirá para abstraer atributos y métodos y hacer que otros usen esa abstracción heredando de ella. Por ende, si intentamos instanciar Animal (new Animal()) nos dará un error de compilación. En esta ocasión es mejor así, pero no todas las superclases deben ser obligatoriamente abstractas, todo depende de tí como programador y de las necesidades que tengas.

Ahora Dog y Cat quedan tremendamente:

class Dog extends Animal {

    public Dog() {

    }

    public Dog(String name, String race, String color, float weight) {
        super(name, race, color, weight);
    }
}

class Cat extends Animal {

    public Cat() {

    }

    public Cat(String name, String race, String color, float weight) {
        super(name, race, color, weight);
    }
}

NOTA: super() lo que hace es llamar al constructor de la clase madre. super.X() donde X es un método llama a la versión de dicho método de la clase madre.

¡Están muy pero muy compactas! Esto es lo que hace la herencia, reutiliza código haciendo mucho mejor nuestro código. Ahora hagamos una pequeña prueba para ver que todo marche bien:

public class Main {

public static void main(String[] args) {

    Animal dog = new Dog("Firulais", "Bulldog", "Marrón", 15.4f);

    Animal cat = new Cat();

    cat.setName("Minino");
    cat.setRace("Angora");
    cat.setColor("Blanco");
    cat.setWeight(4.9f);

    System.out.printf("Nombre del perro: %s", dog.getName());
    System.out.printf("\nPeso del perro: %.2f", dog.getWeight());
    System.out.printf("\nNombre del cato: %s", cat.getName());
    System.out.printf("\nPeso del gato: %.2f\n", cat.getWeight());
}

}

Y efectivamente vemos por pantalla, la misma salida del post anterior:

Nombre del perro: Firulais
Peso del perro: 15,40

Nombre del gato: Minino
Peso del gato: 4,90

Espera, ¿cómo es eso que guardas una instancia de Dog en Animal? ¿cómo es eso posible y para qué sirve?

Para responder a estas interrogante hay que asimilar otro concepto, el de Polimorfismo.

Polimorfismo

El polimorfismo es otra de las características de la programación orientada a objetos que nos dice que un mismo objeto puede tener distintos comportamientos. Para comprender esto en más detalle hagamos una pequeña prueba para poder apreciar como trata Java las superclases y las subclases:

System.out.println(dog instanceof Animal);
System.out.println(cat instanceof Animal);

El operador instanceof verifica si el objeto a la izquierda es del tipo de la derecha. Las dos líneas anteriores preguntan:

¿Es dog instancia de Animal?
¿Es cat instancia de Animal?

Y ambas preguntas se resolverán en true. Ahora, ¿por qué? Pues porque al ser dog y cat subclase de Animal, comparten el mismo ‘árbol genealógico’, son Madre e Hijos. ¿claro?

Para poder observar en mayor detalle el polimorfismo hagamos otra pequeña prueba. Primero editemos el método talk de Animal y hagámoslo abstracto. Esto obligará a todas las subclases a sobreescribir este método, es decir, a sobreescribir el comportamiento original de talk heredado de la superclase (esto lo hacemos así porque obviamente, un gato habla diferente que un perro ;)).

public abstract void talk();

Ahora necesitaremos sobreescribir ese método tanto en Dog y Cat, ya que una clase no abstracta no puede tener métodos abstractos, ya que una clase no abstracta es una clase concreta, bien definida, mientras que una clase abstracta es solamente una ‘idea’, una ‘abstracción’.

Para Dog haremos:

@Override
public void talk() {
    System.out.println("Guarf! guarf!");
}

Y para Cat lo mismo:

@Override
public void talk() {
    System.out.println("Miauuuu miauuuuuuu");
}

Fíjate que ambos métodos tienen encima de ellos una palabra, este tipo de palabras se llaman anotaciones y sirven para indicar cierto tipo de cosas o proveer ciertas características. Este es un tema más avanzado que veremos más adelante.

Ahora sí, hagamos nuestra pequeña y última prueba:

public class Main {

public static void main(String[] args) {

    Dog dog = new Dog("Firulais", "Bulldog", "Blanco", 15.4f);
    Cat cat = new Cat();

    cat.setName("Minino");
    cat.setRace("Angora");
    cat.setColor("Blanco");
    cat.setWeight(4.9f);

    Animal[] pets = {dog, cat};

    // iteramos el array de objetos Animal
    for(Animal pet : pets) {
        String type = pet instanceof Dog ? "Perro" : "Gato";
        System.out.printf("Tipo de animal: %s", type);
        System.out.printf("\nNombre: %s", pet.getName());
        System.out.printf("\nRaza: %s", pet.getRace());
        System.out.printf("\nCOlor: %s", pet.getColor());
        System.out.printf("\nPeso: %.2f\n", pet.getWeight());
        pet.talk();
        System.out.println("\n");
    }

}

}

Vemos como creamos un array de objetos Animal, ya que tanto Dog como Cat son de tipo Animal al heredar de ella. Recorremos el array y primero preguntamos si el objeto Animal actual es también de tipo Dog, si es así, le asigna “Perro” a la variable type, de lo contrario “Gato”. Luego, muestra los atributos de cada Animal. La magia ocurre en la última línea, con la llamada a talk(), para un objeto Dog, el resultado de talk() será completamente diferente que para un objeto tipo Cat, ¡esto es polimorfismo!, la capacidad de un objeto (pet) de tener múltiples comportamientos.

Espero se haya comprendido la lectura. Hasta pronto amigos.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s