web 2.0

sábado, 17 de abril de 2010

Inyección de la Dependencia II: Práctico y Fácil

En la primera parte vimos un ejemplo por demás sencillo, el clásico Hola Mundo! utilizando la Inyección de Dependencia mediante Spring!

En esta segunda parte, continuaremos con otro ejemplo igual de sencillo, más útil, pero que al final de cuentas nos encontraremos con un problema!

Buscando Archivos!
Vamos a realizar un sencillo buscador de archivos! El API de Java incorpora una interfaz llamada FileFilter que contiene al método accept, este método deberá contener toda la lógica requerida para filtrar de una lista de archivos, aquellos que cumplan con nuestras características! De tal modo que pasaremos una referencia de FileFilter al método listFiles de la clase File.

Para empezar, creamos una clase que implemente a la interfaz FileFilter.

package archivos;

import java.io.File;
import java.io.FileFilter;

/**
 *
 * @author windoctor
 */
public class FileExtensionFileFilter implements FileFilter{

    private String extension;

    public FileExtensionFileFilter(String extension){
        this.extension = extension;
    }

    public boolean accept(File pathname) {
        String nameLower = pathname.getName().toLowerCase();

        return (pathname.isFile() &&
                (nameLower.indexOf(extension) > 0)? true:false);
    }    
}

Posteriormente, vamos a crear una clase que se encargue de listar todos los archivos que cumplan con nuestro criterio de búsqueda!

package archivos;

package archivos;

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;

/**
 *
 * @author windoctor
 */
public class BuscadorArchivo {
    private String directorio;
    private String extension;
    private FileFilter filter;

    public BuscadorArchivo(String directorio, String extension){
        this.directorio = directorio;
        this.extension = extension;
    }

    public File[] listar() throws FileNotFoundException{
        File dir = new File(directorio);
        //Si no existe, lanzamos una excepción
        if(!dir.exists())
            throw new FileNotFoundException
                    ("No existe el directorio "+directorio);

        //Este es nuestro filtro, el cual pasaremos al método listFiles
        filter = new FileExtensionFileFilter(extension);
        File[] files = dir.listFiles(filter);

        if(files == null || files.length <= 0){
            System.out.println("No existen archivos "+extension);
            return null;
        }
        else
            return files;
    }
}
Ahora en el applicationContext.xml vamos a agregar la definición de la clase anterior:
<bean id="beanArchivo" class="archivos.BuscadorArchivo">
   <constructor-arg index="0" value="/home/windoctor/Documentos" />
   <constructor-arg index="1" value=".mp3" />
</bean>
Como vemos, la clase BuscadorArchivo tiene un constructor que recibe 2 parámetros de tipo String y que serán inyectados vía Spring. De tal suerte que al ejecutar el siguiente código, obtendremos una lista de archivos:
package archivos;

import java.io.File;
import java.io.FileNotFoundException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

/**
 *
 * @author windoctor
 */
public class ArchivoMain {
    public static void main(String args[]){
        BeanFactory factory = new XmlBeanFactory(
                new FileSystemResource("web/WEB-INF/applicationContext.xml"));
        BuscadorArchivo buscador = (BuscadorArchivo)
                factory.getBean("beanArchivo");
        try {
            File[] files = buscador.listar();
            int size = files.length;
             //Ahora que tenemos todos los archivos filtrados, solo los listamos
            for(int i = 0; i < size; i++){
                File file = files[i];
                String nameFile = file.getName();
                long fileSize = file.length();
                String msg = nameFile + " - Size: "+fileSize;
                System.out.println(msg);
            }
        }
        catch (FileNotFoundException ex) {}
    }
}
Si tenemos archivos mp3 en la carpeta, la consola mostrará una salida similar a esta:

Pista5.mp3 - Size: 4877446
Pista20.mp3 - Size: 3669863
Pista10.mp3 - Size: 10081612

Hasta este punto parece que todo ha salido bien y ya tenemos una idea más clara sobre lo que es la inyección de la dependencia, aunque este ejemplo no es mucho mejor que el anterior y aun nos puede parecer algo tonto la DI. En este punto vamos a realizar una prueba unitaria mediante JUnit! Las pruebas unitarias son algo que muchos no hacemos pese a las enormes ventajas que esto aporta! En NetBeans vamos a agregar a la librería de nuestro proyecto a JUnit 4.5. En la carpeta de "Test Packages" colocaremos la clase de unidad que crearemos a continuación.
package archivo.test;

import archivos.BuscadorArchivo;
import java.io.File;
import java.io.FileNotFoundException;
import org.junit.Assert;
import org.junit.Test;

/**
 *
 * @author windoctor
 */
public class ArchivoTest {

    @Test
    public void listarTest(){
        BuscadorArchivo buscador = new 
                  BuscadorArchivo("/home/windoctor/Documentos", ".mp3");
        try {
            File[] files = buscador.listar();
            Assert.assertNotNull(files);
        } 
        catch (FileNotFoundException ex) {

        }
    }
}

Al ejecutar el código anterior obtendremos una salida de JUnit como la que muestra la imagen de abajo.


Nuestra prueba unitaria ha resultado exitosa! Sin embargo, aún nos falta probar a la clase FileExtensionFileFilter, así que debemos hacer lo mismo.... Un momento... Sin darnos cuenta, ya hemos probado a "FileExtensionFileFilter" dado que BuscadorArchivo obtiene por si mismo una referencia de ese objeto!

En la primera parte de este tutorial, mencionamos que una de las características deseables del Software es que tenga un débil acoplamiento! Desafortunadamente en este ejemplo esta característica no se esta cumpliendo del todo!

Si pensamos un poco más, caeremos en la cuenta que con el ejemplo anterior estamos limitados a filtrar archivos únicamente por su extensión! Pues BuscadorArchivo recupera por si mismo a un FileExtensionFileFilter... ¿Y si deseamos filtrar en base a que un archivo de texto contenga cierta palabra?

El anterior ejemplo es sencillo, pero si lo trasladamos a clases de un Sistema empresarial que es grande y tiene decenas, cientos o miles de clases, las pruebas unitarias se vuelven difíciles de realizar por el fuerte acoplamiento que existe, además que el mantenimiento se complica!

La solución consiste en inyectar objetos FileFilter en BuscadorArchivo, dado que FileFilter es una interfaz, podemos crear tantos filtros de archivos según nuestras necesidades. Realizaremos una modificación a la clase BuscadorArchivo

public class BuscadorArchivo {
    private String directorio;
    private FileFilter filter;

    public BuscadorArchivo(String directorio){
        this.directorio = directorio;
    }

    //Ahora al Buscador de archivos se le asigna un Filtro en lugar
    //de que Él mismo obtenga un filtro.
    public void setFilter(FileFilter filter){
        this.filter = filter;
    }

    public File[] listar() throws FileNotFoundException{
        File dir = new File(directorio);
        //Si no existe, lanzamos una excepción
        if(!dir.exists())
            throw new FileNotFoundException
                    ("No existe el directorio "+directorio);


        File[] files = dir.listFiles(filter);

        if(files == null || files.length <= 0){
            System.out.println("No existen archivos con el criterio de búsqueda");
            return null;
        }
        else
            return files;
    }
}

La diferencia ahora es que BuscadorArchivo ya no recupera por si mismo un filtro, por el contrario, el filtro le es inyectado mediante un método setFilter. Para que Spring pueda hacer esta inyección, debemos modificar también el archivo applicationContext.xml:

<bean id="beanFilter" class="archivos.FileExtensionFileFilter">
<constructor-arg index="0" value=".mp3" />
</bean>

<bean id="beanAnalizadorXml" class="archivos.BuscadorArchivo">
<constructor-arg index="0" value="/home/windoctor/Documentos" />
<property name="filter" ref="beanFilter" />
</bean>

Podemos observar que ahora hemos definido un nuevo bean FileExtensionFileFilter que es inyectado al beanArchivo. El beanArchivo ahora tiene una etiqueta property que consta de 2 atributos, name y ref.


  • name: nombre del atributo de la clase. En este caso, hemos agregado a BuscadorArchivo un atributo llamado filter, este nombre es el que deberá colocarse esta propiedad.
  • ref: Es la referencia de otro bean que se ha declarado en el applicationContext.xml
Con la modificación anterior tenemos  un código con un menor acoplamiento, ahora es posible asignarle realizar búsquedas de archivos con diferentes filtros y no necesariamente por extensión como originalmente sucedía!

Puedes descargar el proyecto en NetBeans de la primera y segunda parte de este tutorial desde aquí.

Cerraremos este tutorial sobre la Inyección de la Dependencia mediante Spring con una tercera y última parte en donde veremos otro ejemplo más que nos permita comprender de mejor manera la DI.

Saludos!!!

Cualquier error encontrado en este tutorial, reportarlo como comentario dentro del mismo!
La información contenida aquí se utiliza con fines educativos y su utilización en ambientes de producción es únicamente responsabilidad de quien lo hace.

1 comentarios:

Anónimo dijo...

molesto el fondo

Publicar un comentario