Tuesday 25 January 2011

Lambdaj Vs Op4j (I)

Hace tiempo que vengo siguiendo estos dos frameworks, y aunque estoy más familiarizado con Lambdaj me apetecía hacer una comparativa entre los dos.

Ambos están orientados a reducir el codigo necesario para la realización de tareas frecuentes que, en código Java normal, nos llevarían más lineas de las que quisieramos.

He realizado una serie de tests hechos con Junit que me han servido para comprobar como se realizan ciertas expresiones en una libreria y en otra. Las pruebas han sido realizadas sobre una coleccion de elementos de tipo Song:



public class Song {

private String artist;
private String genre;
private String title;
private int year;

// Setters y Getters

}



En estos dos frameworks la programación se basa en la utilización de una serie de métodos estáticos disponibles en una clase concreta. En el caso de Lambdaj:




import static ch.lambdaj.Lambda.*;




Y en caso de Op4j:




import org.op4j.Op;



En primer lugar he realizado un filtrado las canciones (lista obtenida de Util.songs) que contengan en su titulo (getTitle()) la cadena "en":

En Lambdaj:



@Test
public void testListByNameSong(){
List<song> songsByNameContaining =
filter(
having(on(Song.class).getTitle(),containsString("en")),
Util.songs
);
TestCase.assertEquals(2,songsByNameContaining.size());
}



En Op4j



@Test
public void testListByNameSong() throws Exception{
List<song> songsByNameContaining =
Op.on(Util.songs).removeAllFalse(new IFunction<song, boolean>(){
@Override
public Boolean execute(Song input, ExecCtx ctx)throws Exception {
return input.getTitle().contains("en");
}
}).get();
TestCase.assertEquals(2,songsByNameContaining.size());
}



En esta primera comparativa parece mucho mas efectiva la aproximación de Lambdaj en cuanto a la brevedad del código.

En la siguiente prueba he repetido el filtrado de elementos esta vez por un rango de fechas:

Lambdaj:



@Test
@SuppressWarnings("unchecked")
public void testListByYearRange() throws Exception{
List<song> songsBetweenDates =
filter(allOf(
having(on(Song.class).getYear(),greaterThan(1984)),
having(on(Song.class).getYear(),lessThan(2005))
), Util.songs);
TestCase.assertEquals(7,songsBetweenDates.size());
}



Op4j



@Test
public void testListByYearRange() throws Exception{
List<song> songsBetweenDates =
Op.on(Util.songs).
removeAllFalse(new IFunction<Song, Boolean>() {
@Override
public Boolean execute(Song arg0, ExecCtx arg1) throws Exception {
return arg0.getYear() > 1984 && arg0.getYear() < 2005;
}
}).
get();
TestCase.assertEquals(7,songsBetweenDates.size());
}



Otra vez nos encontramos con una diferencia de lineas de código importante. No obstante cuanto más se complican las expresiones mas me cuesta la codificación en Lambdaj además de que empiezan a aparecer warnings de "unchecked" que no puedo resolver. Op4j en este sentido es menos complejo aunque haya que codificar unas cuantas lineas más.

Bueno ahora pasamos a algo mas ameno que es la agregación de datos de una lista, en ambos casos he sumado los años de todas las canciones de mi lista. En esta ocasión ambas soluciones son sencillas y claras:

Lambdaj



@Test
public void testAggregateYears(){
int sumOfYears = Lambda.sum(Util.songs,on(Song.class).getYear());
TestCase.assertEquals(23928,sumOfYears);
}



Op4j



@Test
public void testAggregateYears(){
int sumOfYears =
Op.on(Util.songs).map(Get.attrOfInteger("year")).exec(FnInteger.sum()).get();
TestCase.assertEquals(23928,sumOfYears);
}



Por ultimo (de momento) he realizado una ordenación de elementos por dos atributos de la clase Song, "artist" y "title". Para mi, en este ultimo ejemplo es mas clara la solución de Op4j a la hora de afrontar este problema:

Lambdaj


@Test
public void testSortingByTwoProperties(){
List<song> sortedSongs =
sort(
sort(Util.songs,on(Song.class).getArtist()),
on(Song.class).getTitle()
);
TestCase.assertEquals("Crazy Train",sortedSongs.get(0).getTitle());
}



Op4j


@Test
public void testSortingByTwoProperties(){
List<song> sortedSongs =
Op.on(Util.songs).
sortBy(Get.attrOfString("artist")).
sortBy(Get.attrOfString("title")).
get();
TestCase.assertEquals("Crazy Train",sortedSongs.get(0).getTitle());
}




Conclusión:

  1. Lambdaj
  • Pros: Flexibilidad
  • Cons: Complejidad proporcional a la expresión, rendimiento mas bajo (inapreciable en programacion normal)

  1. Op4j
  • Pros: Sencillez
  • Cons: Limitado. Para operaciones sencillas sobre colleciones.

En principio me sigue gustando más Lambdaj por sus posibilidades a la hora de resolver problemas sobre colecciones.

Las páginas de ambos proyectos

Op4j : http://www.op4j.org/
Lambdaj: http://code.google.com/p/lambdaj/

La segunda parte de esta revisión esta en "Lambda Vs Op4j (y II)"

No comments:

Post a Comment