Patrón Visitor con commons-collections y sus Closures

En este tutorial vamos a ver cómo podemos usar la librería de Apache commons-collections para implementar de forma sencilla un Visitor que se recorra todos los elementos de una colección.

Creación: 13-01-2010

Índice de contenidos

1.Introducción
2. Entorno
3.El ejemplo sencillo
4.El ejemplo no tan sencillo
5.Conclusiones
6.Sobre el autor


1. Introducción

Básicamente el patrón Visitor es el que nos permite separar el recorrido de un grafo, de la operación que se hará con cada uno de los nodos de ese grafo.

Este patrón de diseño nos permite añadir nuevos tratamientos sobre los nodos del grafo, o añadir nuevos recorridos, de forma independiente y sin que uno afecte al otro.

En este tutorial vamos a ver como podemos usar la librería de Apache commons-collections (http://commons.apache.org/collections) para implementar de forma sencilla un Visitor que se recorra todos los elementos de una colección.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 17′ (2.93 GHz Intel Core 2 Duo, 4GB DDR3 SDRAM, 128GB Solid State Drive).
  • NVIDIA GeForce 9400M + 9600M GT with 512MB
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.1
  • JDK 1.6.0_17
  • Maven 2.2.1
  • commons-collections 3.2.1


3. El ejemplo sencillo

Dentro de la librería commons-collections nos encontramos con la clase

Text
CollectionUtils

, y dentro de esta podemos ver el método estático

Text
forAllDo()

. Este método tiene dos
parámetros de entrada:

  • la colección que queremos recorrer. Como bien dice el nombre del método, se recorrerá todos los elementos de la colección.
  • una
    Text
    Closure

    . Esta clase se llamará con cada elemento de la colección, y es aquí donde haremos el tratamiento que nos interese.

Un ejemplo sencillo sería mostrar por consola cada uno de los elementos de la colección:

CollectionUtils.forAllDo(miColeccion, new Closure() {
    public void execute(Object input) {
        System.out.println(input.toString());
    }
}

Evidentemente para hacer esto podríamos haber hecho un bucle por la colección e imprimir los elementos. Pero en tal caso, el recorrido y el tratamiento estarían ligados, y es precisamente lo que estamos intentado evitar en este tutorial.

4. El ejemplo no tan sencillo

Imaginemos ahora que tenemos dos clases que no tienen ningún tipo de jerarquía entre ellas. Ambas clases tienen un valor numérico, y queremos obtener la media de este valor para los objetos de cada uno de estos dos tipos:

public class Foo {
    public double getFooValue() {...}
}
public class Bar {
    public double getBarValue {…}
}

 

Vamos a hacer una clase que sea capaz de, dada una colección de objetos de tipo

Text
Foo

o tipo

Text
Bar

, podamos calcular la media del valor de todos los objetos de la colección.

public class AverageCalculator<T> {

    private double sum;

    private class AverageTransformer implements Transformer {   
        public Object transform(Object input) {
            sum += ((Double)input).doubleValue();
            return null;
        }
    };

    public double calculate(Collection<T> collection, Transformer valueAccessor) {
        if (collection.isEmpty()) return 0;

        sum = 0;
        CollectionUtils.forAllDo(collection, ClosureUtils.asClosure(
                TransformerUtils.chainedTransformer(valueAccessor, new AverageTransformer())));
        return sum / collection.size();
    }
}

Vemos que estamos usando la interfaz

Text
Closure

y la clase 

Text
Transformer

. La diferencia básica entre estas dos interfaces es que el método

Text
execute()

de

Text
Closure 

no devuelve ningún valor, mientras que el método

Text
transform() 

de

Text
Transformer

devuelve un

Text
Object

.

La gracia está en cómo hemos unido las dos transformaciones con el método

Text
chainedTransformer()

. De esta manera el resultado del

Text
Transformer

valueAccessor se pasará como parámetro de entrada (input) al siguiente

Text
Transformer

. Es decir, el primer

Text
Transformer

obtendrá el valor del elemento de la colección y este valor se pasará al

Text
AverageTransformer

que se encargará de ir acumulándolo.

 

Para usar esta clase bastará con hacer:

final double averageFooValues = new AverageCalculator<Foo>().calculate(coleccionObjetosFoo, new Transformer() {
    public Object transform(Object input) { return Double.valueOf(((Foo)input).getFooValue()); }
});
final double averageBarValues = new AverageCalculator<Bar>().calculate(coleccionObjetosBar, new Transformer() {
    public Object transform(Object input) { return Double.valueOf(((Boo)input).getBooValue()); }
});

 

De hecho lo que acabamos de construir es una clase capaz de sacar la media de los elementos de una colección, independientemente de su tipo. Sólo necesitamos saber como extraer el valor de cada elemento y crear el Transformer adecuado.

5. Conclusiones

El ejemplo quizás parece un poco complejo, pero realmente el diseño es muy potente (intenta seguir los principio de SOLID,
http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx).
De forma que podemos crear fácilmente nuevas clases para, en vez de calcular la media, calcular cualquier otra cosa sobre los elementos. O podemos fácilmente crear un nuevo recorrido (por ejemplo sólo tratar los elementos pares) y usar este nuevo recorrido en vez del método

Text
forAllDo()

.

Es fundamental conocer las librerías que están a nuestro alcance y un poquito de patrones. Con esto podemos hacer diseños muy interesantes escribiendo muy poco código.

6. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L. – «Soporte a Desarrollo»

http://www.autentia.com

 

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad

Información básica acerca de la protección de datos

  • Responsable: IZERTIS S.A.
  • Finalidad: Envío información de carácter administrativa, técnica, organizativa y/o comercial sobre los productos y servicios sobre los que se nos consulta.
  • Legitimación: Consentimiento del interesado
  • Destinatarios: Otras empresas del Grupo IZERTIS. Encargados del tratamiento.
  • Derechos: Acceso, rectificación, supresión, cancelación, limitación y portabilidad de los datos.
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad

Alejandro es socio fundador de Autentia y nuestro experto en Java EE, Linux y optimización de aplicaciones empresariales. Ingeniero en Informática y Certified ScrumMaster. Seguir @alejandropgarci Si te gusta lo que ves, puedes contratarle para darte ayuda con soporte experto, impartir cursos presenciales en tu empresa o para que realicemos tus proyectos como factoría (Madrid). Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación.

¿Quieres publicar en Adictos al trabajo?

Te puede interesar

10/06/2025

Iván Suarez Romero

Aprende cómo migrar tu sitio Joomla 3 a Joomla 5 de forma segura, manteniendo el diseño, la funcionalidad y compatibilidad con extensiones. Una guía paso a paso con recomendaciones, imágenes y buenas prácticas para actualizar sin sorpresas.

04/06/2025

Gonzalo Matarrubia González

Descubre qué es Yocto Project, sus ventajas, usos reales en Izertis y cómo crear tu propia distribución Linux para Raspberry Pi paso a paso, de forma sencilla y flexible.

30/05/2025

Roberto José

¿Trabajas con Drupal y SonarQube 9.9? En este artículo exploramos cómo adaptar el análisis estático para evitar falsos positivos, desactivar reglas conflictivas del Quality Profile y delegar el estilo a PHP CodeSniffer. Una guía práctica para mejorar la integración sin depender aún de SonarQube 10.