Introducción a Groovy (IV)

En esta entrega del tutorial de introducción a Groovy del tutorial de introducción a Groovy vamos a ver que son y como funcionan los POGO's (Plain Old Groovy Objects). Los POGO's son el sustituto natural de Groovy a los POJO's de Java, tomando toda la funcionalidad y potencia de Groovy para construir objetos.

4.1 POGO's

Los POGO's, al igual que los POJO's en Java, son (cita de la Wikipedia):

clases simples que no dependen de un framework en especial

Esto es, clases que no heredan ni implementan ninguna clase/interface de un API en concreto y externo a nuestra aplicación, y que por tanto pueden ser reusados una y otra vez entre aplicaciones. Este concepto de reutilización de clases es uno de los pilares básicos de la programación orientada a objetos. A efectos practicos, un POGO es una clase normal y corriente:

class Libro {
    String titulo
    String autor
    int numPaginas
} 
	

La clase del código anterior define un libro con tres atributos que pueden ser leidos/escritos directamente, ya que Groovy generará los métodos getter/setter por nosotros en tiempo de compilación. Es más, a pesar de que dichos métodos estarán disponibles cuando compilemos y llamemos a la clase, podemos llamar a los atributos directamente por su nombre:

def libro = new Libro()
libro.titulo = "Effective Java 2nd Edition"
libro.autor = "Joshua Bloch"
libro.numPaginas = 346
	

Esta sintaxis, basada en la notación de puntos (objeto.atributo) es también válida para leer valores:

println libro.titulo
	

4.2 Getters y Setters

En ciertas ocasiones, sin embargo, debemos controlar como se almacenan y leen los valores de los atributos. Nada tan sencillo como sobreescribir el getter/setter correspondiente:

class Libro {
    String titulo
    String autor
    int numPaginas
    
    String getTitulo() {
        return titulo.toUpperCase()
    }
}

def libro = new Libro()
libro.titulo = "Introducción a Groovy"
libro.autor = "David Marco"
libro.numPaginas = 0

println libro.titulo 
	

En la definición de la clase Libro del código anterior hemos proporcionado explícitamente un método setter para el atributo titulo, de manera que todas las llamadas a libro.titulo serán redirigidas a través de este método customizado. Es probable que hayas notado algo rato en la definición del método anterior (vuelve atrás y miralo bien...). ¿Ya? Como seguramente has observado, el método no es público, y la norma general es que los métodos getter/setter (sobre todo los getter) sean public, ya que de otra manera nuestra clase sería muy poco usable. Esto nos lleva a la siguiente sección de este artículo.

4.3 Niveles de acceso

En Groovy prima en cierto modo el concepto de Convención sobre Configuración; esto es, que la sintaxis más simple refleja el comportamiento mas común de un componente. Lo vimos en el punto anterior con los getters y setters por defecto y tambíén aplica de la siguiente manera a los niveles de acceso por defecto en POGO's:

  • Todas las clases son públicas por defecto
  • Todos los métodos son públicos por defecto
  • Todos los atributos son privados por defecto

Estas reglas, sin embargo, conllevan ciertas matizaciones que debemos comprender. Veamos un ejemplo:

class Libro {
    String titulo
    String autor
    private int numPaginas
} 
	

En el ejemplo anterior, hemos definido explícitamente el atributo numPaginas como privado. Esto indica al compilador que no debe generar métodos getter/setter para este atributo, de manera que el atributo no será visible de ninguna manera para un cliente Java. Y digo solamente clientes Java porque Groovy puede seguir accediendo al atributo, para leerlo y escribirlo. Esto es debido a que Groovy tiene ciertos privilegios al usar otro código Groovy, llamemoslo Despotismo Groovy. Incluso aunque proporcionemos métodos setter/getter privados para ese atributo, ¡Groovy podrá seguir accediendo a el!. Un efecto secundario de este comportamiento es que el nivel de visibilidad default-package (el que se venia aplicando hasta ahora en Java a todo método, campo y clase interna sin un nivel de visibilidad concreto) no existe en Groovy. No voy a discutir aquí los motivos por los que el lenguaje funciona de esta manera ni mucho menos a opinar al respecto, ambos temas caen fuera de los propositos de este tutorial. Sin embargo, debes tener muy presente este comportamiento cuando uses el lenguaje. Y como he dicho antes, desde Java el código se comportará como lo ha venido haciendo hasta ahora, respetando los niveles de acceso (que para eso están ahí), por lo que aún puedes controlar la visibilidad de los miembros de una clase escrita en Groovy (salvo el ya mencionado package-default que deja de existir) desde Java.

Un privilegio extra de Groovy es que puede leer y escribir un atributo directamente, sin pasar por el método getter/setter correspondiente (sea este generado implícitamente por el compilador o explícitamente por nosotros):

println libro.@titulo
	

En la sentencia anterior hemos utilizado el caracter @ para indicar que debemos acceder a la variable titulo directamente.

Por último, y aunque no está relacionado con la discusión sobre niveles de acceso, vamos a ver una tercera manera de leer y escribir atributos en un POGO:

def libro = new Libro()
libro.setProperty('titulo', 'Introducción a Groovy')
println libro.getProperty('titulo') 
	

La sintaxis anterior puede resultar muy útil en ciertos casos, por ejemplo cuando no disponemos en tiempo de compilación de los nombres de los atributos que tenemos que llamar, pero si en tiempo de ejecución.

4.4 Constructores

Groovy nos ofrece una sintaxis especial para crear on POGO en una única linea:

def libro = new Libro(titulo:'>Effective Java 2nd Edition', autor:'Joshua Bloch', numPaginas:346) 
	

Si recuerdas el tercer artículo del tutorial, te habrás dado cuenta que los valores que se han pasado al constructor son en realizad una estructura de tipo Map. Por tanto, también podemos pasar al constructor un mapa previamente definido:

def mapa = [titulo:'Effective Java 2nd Edition', autor:'Joshua Bloch', numPaginas:346]
def libroGrails = new Libro(mapa) 

println mapa.titulo 
	

Pero, ¡aún hay más! Esta sintaxis es válida incluso para crear un POJO (un objeto Java), así que nos podemos beneficiar de ella cuando estemos usando objetos creados en Java. Ahora es el momento de preguntarnos: ¿Estamos obligados a pasar todos los atributos de un objeto al construir dicho objeto?. La respuesta es NO:

def libro = new Libro(titulo:'Effective Java 2nd Edition') 
	

El resto de los atributos pueden ser establecidos en cualquier momento posterior a la creación del objeto y de cualquiera de las maneras que hemos visto hasta ahora. Así de simple. Esto nos lleva a otra pregunta: ¿Debemos pasar los atributos en algún tipo de orden? La respuesta es, de nuevo NO: cualquier orden es válido.

Otra poderosa característica de los constructores en Groovy es que podemos proporcionar a cualquier atributo un valor por defecto si ninguno es proporcionado:

class Libro {
    String titulo
    String autor
    int numPaginas
    
    Libro(int numPaginas, String titulo='Sin título', String autor='Sin autor') {
        this.titulo = titulo
        this.autor = autor
        this.numPaginas = numPaginas;
    }
}

def libro = new Libro(10)
println libro.titulo 
	

En el ejemplo anterior, se ha definido un constructor que además de inicializar los valores de los atributos de la clase, declara dos de ellos como opcionales dandoles un valor por defecto (titulo y autor). Por tanto, si instanciamos un objeto Libro y no proporcionamos los parámetros que corresponden a dichos atributos opcionales, se tomarán sus valores por defecto. Además, en la declaración del constructor, los parámetros opcionales deben aparecer después de los parámetros obligatorios para ese constructor. A la hora de diseñar el constructor debes ordenar cuidadosamente los parámetros opcionales, de manera que los que tienen más posibilidad de ser proporcionados deben aparecer primero (aunque esta anticipación al uso de la clase no es siempre posible). Nuestra nueva clase Libro es perfecta para mostrarnos este asunto:

def libro = new Libro(346, 'Joshua Bloch')
println libro.titulo 
	

Mira bien el código anterior: ¿Que atributo estamos ajustando con el parámetro 'Joshua Bloch'? ¿titulo o autor (recuerda que ambos son String y ambos son opcionales)?. En este caso concreto, es titulo, aunque mi ingenua intención era ajustar el atributo autor, y por tanto he construido un objeto que, cuando sea usado, no se comportará como yo espero. Piensa cuidadosamente en el orden de los parámetros opcionales y, más aún, usalos con mucho cuidado y en situaciones donde realmente son necesarios y/o útiles.

Con este consejo termina la cuarta entrega del tutorial de introduccíón a Groovy (un poquito más corto de lo habitual, pero espero que igual de interesante). En el próximo entículo del tutorial abordaremos la Metaprogramación en Groovy, un estilo de programar realmente increible. ¡Hasta entonces!