Herencia y Polimorfismo, parte I.


Me había decidido a escribir sobre patrones de diseño ya que es un tema que actualmente estoy estudiando, sin embargo una de las primeras cosas que aprendí con los patrones de diseño es a favorecer la composición (HAS A) sobre la herencia (IS A), con lo cual quizás si no se conoce lo suficiente se subestime el poder de la herencia, así que lo mejor será comenzar con los principios básicos de programación, aunque por el momento solo tengo planeado abarcar la herencia y el polimorfismo.

Los principios básicos de la programación orientada a objetos son:
·         Abstracción
·         Encapsulación
·         Polimorfismo
·         Herencia

Conocer estos principios no te hará un buen programador, pero él no conocerlos bien definitivamente será un obstáculo para seguir avanzando como programador. La abstracción a grandes rasgos te permite ignorar el "cómo funciona" y concentrarte en "que hace", dicho de otra manera no necesitas conocer el funcionamiento de un objeto para hacer uso del mismo, como sucede en algunos casos cotidianos, por ejemplo tu no necesitas saber cómo funciona internamente una computadora para utilizarla, lo mismo sucede con la abstracción no es necesario saber cómo funciona un objeto para hacer uso del mismo.

public class ComputerUser
{
 private Computer computer;
 
 public ComputerUser(Computer computer)
 {
  this.computer = computer;
 }
 
 public Document createDocument(String name)
 {
  return this.computer.createDocument(name);
 }
 
 public void writeDocument(Document doc, String text)
 {
  this.computer.writeDocument(doc, text);
 }
 
 public void closeDocument(Document doc)
 {
  this.computer.Close();
 }
}


En este caso nosotros no conocemos como el objeto "computer" realiza su trabajo sin embargo conocemos sus métodos y los utilizamos, pero no entraré en más detalles.

Encapsulamiento básicamente se refiere al aislamiento de los métodos y propiedades del objeto, a los cuales no tendremos acceso sino solamente mediante métodos específicos definidos por el objeto, retomando el ejemplo anterior podemos tener acceso al objeto "computer" pero no tenemos acceso al atributo "ram", ya que podría ser peligroso que se tenga acceso a la misma, tampoco entraré en más detalles.

La herencia que es nuestro tema principal consiste en la reutilización de código de una clase a otra, es decir una clase que deriva de otra obtiene algunos de los atributos y métodos de la primera, existe la herencia simple y la herencia múltiple, como nos concentraremos en Java solo trataremos la herencia simple.

El Polimorfismo es la habilidad de un método de comportarse de diferentes formas, es una de las características más usadas en la programación orientada a objetos, es el concepto que más se me dificulta explicar así que lo explicare más a detalle en código.

En ocasiones cuando codificamos tenemos una tendencia a repetir código de una clase a otra, lo cual a futuro se vuelve un dolor de cabeza, ya que un cambio a ese código significa modificar más de una clase, aunque esto se puede evitar con un buen diseño orientado a objetos, veamos un ejemplo más práctico, ¡fabriquemos un carro!, fabriquemos el clásico tsuru.

package CarTest;

public class Tsuru
{
 private String model;
 private int year;
 
 public Tsuru()
 {
  this.model = "Tsuru";
  this.year = 2012;
 }
 
 public void turnOn()
 {
  System.out.println("Start the engine");
 }
 
 public void turnOff()
 {
  System.out.println("Stop the engine");
 }
 
 public void accelerate()
 {
  System.out.println("Speed Up");
 }
 
 public void brake()
 {
  System.out.println("Stop the Car");
 }
 
 public void speedChange()
 {
  System.out.println("Change the velocity with Clutch");
 }
 
 public String getModel()
 {
  return this.model;
 }
 
 public int getYear()
 {
  return this.year;
 }
 
 public String toString()
 {
  return "I'm a " + this.model + " of " + this.year;
 }
}

Perfecto tenemos nuestro tsuru, este puede acelerar, frenar y cambiar de velocidad, tiene todo lo necesario para dar una vuelta, lo cual es tentador pero primero fabriquemos otro carro, esta vez un chevy... Un momento prácticamente tendremos que repetir todo el código de tsuru a chevy ya que son capaces de realizar las mismas acciones, repetir el código suena a una tarea tediosa y aburrida, así que lo mejor será buscar otras alternativas es momento de hacer uso de la herencia.

Por cierto si te preguntas por la palabra "this" está es utilizada para hacer referencia a la instancia actual, e igual lo tratare en otro post.

Creamos una clase abstracta que contenga los métodos que comparten "turnOn", "turnOff", "accelerate", "brake", "speedChange", "getModel", "getYear", "toString", un momento... nuestro carro puede ser automático o estándar, eso significa que nuestro método "speedChange" va a variar dependiendo del tipo carro, podemos dejarlo así y confiar en que el programador que ocupe esta clase abstracta se acuerde de modificar el método en un carro automático, eso no suena muy bien, lo mejor será obligar al programador a escribir su propia implementación haciendo al método abstracto, eso suena mucho mejor, así no tendremos que preocuparnos por un carro automático intentando cambiar de velocidad con un Clutch inexistente.

package CarTest;

public abstract class Car
{
 private String model;
 private int year;
 
 public Car(String model, int year)
 {
  this.model = model;
  this.year = year;
 }
 
 public void turnOn()
 {
  System.out.println("Start the engine");
 }
 
 public void turnOff()
 {
  System.out.println("Stop the engine");
 }
 
 public void accelerate()
 {
  System.out.println("Speed Up");
 }
 
 public void brake()
 {
  System.out.println("Stop the Car");
 }
 
 public abstract void speedChange();
 
 public String getModel()
 {
  return this.model;
 }
 
 public int getYear()
 {
  return this.year;
 }
 
 public String toString()
 {
  return "I'm a " + this.model + " of " + this.year;
 }
}

Quizás te preguntarás porque en el constructor de la clase abstracta "Car" recibe de parámetros el Modelo y el año, esto es porque en Herencia los atributos y métodos que se heredan son los marcados con el "scope" public, protected y default, los públicos pueden ser heredados por cualquier otra clase dentro o fuera del "package", los default solo por clases dentro del "package" y los protected por clases dentro del "package" y por clases fuera del "package" pero solo a la primer subclase, hablaré de esto en otra ocasión. Así que no podemos modificar los atributos modelo y año directamente ya que son privados, así que delegamos esa responsabilidad al constructor que si puede modificar estos atributos.

Ahora modifiquemos nuestra clase Tsuru para que herede de Car, y creemos nuestra clase Chevy.

package CarTest;

public class Tsuru extends Car
{
 public Tsuru()
 {
  super("Tsuru", 2012);
 }
 
 public void speedChange()
 {
  System.out.println("Change the velocity with Clutch");
 }
}

package CarTest;

public class Chevy extends Car
{
 public Chevy()
 {
  super("Chevy", 2008);
 }
 
 public void speedChange()
 {
  System.out.println("Automatic change of velocity");
 }
}

Al llamar al método "super" lo que hacemos es llamar al constructor de la clase base, es decir el constructor de la clase que heredamos, en este caso la clase Car, y le pasamos los argumentos modelo y año.

Perfecto nuestras clases ahora son bastante compactas y tienen todas las funcionalidades de un carro, por el momento dejaremos el tema hasta aquí, pero seguiremos con él en la segunda parte de este post, ya que no hemos terminado con la herencia, no hemos visto polimorfismo, y además tenemos que probarar nuestro carros.

Happy Coding...

1 comentario: