Java

Entendiendo definitivamente el uso de Interfaces

Mucha información podemos encontrar en internet sobre el concepto y ejemplos de uso de una Interfaz en Java. Se nos dice que las interfaces simulan la herencia múltiple, que son buena técnica para desacoplar el código, etc., sin embargo la gran mayoría de esos ejemplos se limitan a algo como esto y terminamos por no entender realmente el uso real y el poder de las interfaces.

Es claro que el ejemplo anterior es muy sencillo y se puede entender fácilmente, pero aun así seguimos sin entender ¿Por qué usar interfaces? En el ejemplo anterior, ¿Cuál es la diferencia entre usar interfaces y no haberlas usado? Bueno, en este artículo trataré de dar respuesta a ello.

 

Ordenar un arreglo de objetos…

Resulta que en nuestra empresa existen muchísimos flujos por donde viaja la información. El departamento «X» nos deposita en una interfaz que es un archivo de texto, una lista de proveedores externos, en otro archivo nos deposita una lista de productos, etc., información que nosotros debemos mostrar de forma ordenada en un reporte a nuestros usuarios. Así que comenzamos por escribir nuestra primera clase llamada «Despacho»

 

 

Adicional a lo anterior, le creamos setters y getters a cada uno de los atributos de la clase (razonSocial, RFC, etc.) y sobrescribimos el método toString.

 

Esta clase Despacho representa a un proveedor de nuestra empresa. Así que ahora debemos pensar en cómo vamos a ordenar objetos de este tipo. Se me ocurre que vamos a crearle a la clase Despacho el siguiente método.

 

 

¿¿¿Y lo anterior que es???… Bueno, sencillamente es un método que me compara en base al id Despacho el despacho actual contra otro despacho que recibe como parámetro y si el despacho actual tiene un ID mayor al del despacho comparado entonces me retornara un 1, en caso de que el ID del despacho actual sea menor que el del ID del despacho comparado me retorna -1 y en caso de ser iguales me retorna cero. Por el momento no pregunten el por qué de este método.

Posteriormente debemos crear alguna clase que me implemente la funcionalidad de ordenar, así que vamos a crear una clase llamada «Ordenador» y ya que el fin de este tutorial no es explicar algoritmos de ordenamiento, nos vamos a fusilar el código de esta página http://codigomaldito.blogspot.mx/2007/11/ordena-burbuja-en-java.html y lo vamos a implementar en nuestra clase ordenador con algunas ligeras modificaciones, de tal forma que tengamos algo como esto:

 

 

Como podemos observar, el método estático «ordenar» recibe un arreglo de objetos Despacho y en la línea 25 vemos como manda a llamar al método «comparar» que definimos antes.

Ahora ya solo nos resta crear una clase para probar lo anterior y ahí mismo definir objetos despacho simulando que en realidad los estamos leyendo de un archivo de texto.

 

Al correr el ejemplo anterior vemos que nos imprime los objetos desordenados ya que aun no mandamos a llamar al método «ordenar» de la clase Ordenador.

 

Ahora vamos a mandar a llamar al método ordenar, ya que se trata de un método estático, no será necesario crear una instancia de la clase Ordenador.

 

Al correr nuevamente el ejemplo y verificar la salida veremos que ya tenemos los elementos ordenados…

 

Hasta aquí todo va muy bien… ya podemos presentar un reporte al usuario con la información ordenada…. Pero ahora necesitamos ordenar otras clases llamadas Productos, Gestores, etc…. Dedique un momento antes de seguir leyendo como podría implementar esta funcionalidad…

Una de las probables cosas que se nos ocurren es crear un método de ordenamiento para cada objeto que necesitemos, pero obviamente esto es una pésima idea!! ¿¿Se imagina cuanto código duplicado tendríamos??

 

Aquí es donde las interfaces llegaran a nuestro rescate. Vamos a crear una interfaz genérica llamada «Comparable».

 

Ahora, nuestra clase Despacho que hemos definido anteriormente, deberá implementar esta interfaz.

La clase despacho ya tenía un método llamado «compare», así que ahora al implementar Comparable ese método pasara a ser la implementación.

Finalmente, vamos a modificar la clase Ordenador para que en lugar de recibir objetos Despacho, reciba un arreglo de objetos Comparable.

 

Con esto ahora ya podemos ordenar cualquier objeto que implemente nuestra interfaz Comparable. Así que no importa si ya tenemos 10 clases que necesitamos ordenar, lo único que tendremos que hacer es implementar Comparable en cada una de ellas y definir el criterio de ordenamiento. Creamos una clase llamada Gestor como a continuación se muestra.

 

Note que tenemos nuevamente el método «compare» pero ahora hemos invertido las condiciones. Con esto ahora conseguiremos tener un ordenamiento descendente.

 

Y la salida al ejecutarlo es:

 

Y ahora necesitamos ordenar productos pero ya no por ID sino por nombre… No importa, a nuestra clase Producto le implementamos Comparable y definimos en el método «compare» el criterio de ordenamiento que sería por nombre.

Todo el ejercicio anterior ha sido un ejemplo muy claro del poder de las interfaces y lo que realmente pueden hacer por nosotros… Aunque bueno, he hecho un poco de trampa puesto que el mismo API de java ya define básicamente clases que hacen lo anterior… Veamos.

En Java ya existe una interfaz Comparable dentro del paquete java.lang que define un método llamado «compareTo». Vamos a ir a la clase Despacho y a quitar nuestra propia interfaz Comparable y sustituirla por la del paquete java.lang.

Y definimos el método «compareTo» (Ojo, no se confunda!!! Nuestro método se llamaba solo «compare» y que nombre así muy a propósito. El método compareTo es de la interfaz Comparable del paquete java.lang.)

 

 

Finalmente les presento a la clase Collections del API de java. Esta clase tiene un método llamado «sort» que recibe una lista de objetos que queremos ordenar, pero para que este ordenamiento funcione, estos objetos deben implementar a la interfaz Comparable del API de java.

 

 

Uffff… pues vaya que trabajamos arduamente! Ya existiendo una clase Collections, nosotros creamos nuestra propia versión de dicha clase y la nombramos Ordenador. Pero a final de cuentas esto nos sirvió para entender más a las interfaces en Java.

 

General

Modificadores de acceso

Existen 4 modificadores de acceso:

  1. public
  2. private
  3. protected
  4. default

    

En una relación de herencia es importante diferenciar dos cosas:

    1. Una subclase puede invocar a un método de la clase padre

    2. Una subclase puede heredar a un método de su clase padre.

 

Para ver la diferencia anterior y conocer de mejor manera los modificadores de acceso, hagamos el siguiente ejercicio.    

Creamos una clase llamada Padre con el método público presentarse.

 

Creamos una subclase de Padre.

    

Ejecutamos nuestro ejemplo y observamos los resultados que no tienen nada de anormal.

Ahora vamos a crear otro paquete llamado «otropaquete» y a copiar la clase Hijo en ese nuevo paquete. Ambas clases Hijo correrán sin problema y darán el mismo resultado.

 

Posteriormente vamos a quitar el modificador de acceso «public» que tiene el método presentarse() de la clase Padre. Cuando un método no tiene modificador de acceso, toma el modificador «default».

 

Con esto ahora tendremos un error en una de las clases y es sencillamente por qué el modificador «default» solo tiene un alcance dentro del mismo paquete.

 

Finalmente cambiemos el modificador de acceso «default» y pongámosle «protected»

 

Anteriormente teníamos 2 errores, uno en la línea 20 y otro en la línea 24, pero al cambiar a protected ahora observamos que solo existe un error en la línea 24! El de la línea 20 desapareció!! ¿Por qué?

La explicación es sencilla. El modificador protected tiene alcance a nivel de «paquete» y para aquellas clases fuera del paquete pero solo por Herencia. Cuando hay herencia los métodos de la clase padre se hacen propios de la clase hija, es decir, aunque no los vemos, esos métodos existen ahí en la clase hija. Por ello en la línea 20 ya no tenemos ningún error puesto que el modificador protected permite que el método sea heredado. Sin embargo, en la línea 24 continuamos teniendo un error ya que ahí no estamos llamando al método heredado, sino estamos invocando el método de la clase padre.

En conclusión:

  1. public.        Acceso desde cualquier clase de cualquier paquete
  2. protected.    Acceso desde cualquier clase solo del mismo paquete ó fuera del paquete pero por medio de Herencia.
  3. default.    Similar al protected pero no permite el acceso fuera del paquete ni siquiera por Herencia.
  4. private.    No hay que ser un experto ni adivino para deducir su efecto, pero esto que quede como tarea.