Telescoping constructor anti-pattern & Builder pattern

Cuando nuestros objetos crecen, no sólo los métodos equals() y hashCode() pueden volverse inmanejables, los constructores también pueden volverse un arma de doble filo. Instanciar un objeto con tres o cuatro parámetros es sencillo mediante su correspondiente constructor, en cambio, con objetos con un número mayor de propiedades la misma solución se vuelve poco práctica.

Vamos a ver un ejemplo práctico, imaginar que decidimos crear nuestra propia clase para manejar fechas. Nuestra clase, a la que llamaremos ‘Date’, contará con siete propiedades tal que así:


public class Date {

	private int year;

	private int month;

	private int day;

	private int hour;

	private int minute;

	private int second;

	private int millisecond;

}

¿Cómo queremos que los objetos de nuestra clase ‘Date’ sean creados?

Algo habitual podría ser el crear constructores para los casos más habituales, algo de este estilo:


	// Creamos un constructor para las combinaciones
	// más habituales
	public Date(int year, int month, int day) {
		this(year, month, day, 0, 0, 0, 0);
	}

	public Date(int year, int month, int day, int hour) {
		this(year, month, day, hour, 0, 0, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute) {
		this(year, month, day, hour, minute, 0, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute, int second) {
		this(year, month, day, hour, minute, second, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute, int second,int millisecond) {
		super();
		this.year = year;
		this.month = month;
		this.day = day;
		this.hour = hour;
		this.minute = minute;
		this.second = second;
		this.millisecond = millisecond;
	}

De esta manera tendríamos cubiertos los casos más típicos para esta clase y a la hora de instanciar una clase en nuestro IDE nos aparecería algo así:

Date01

Por otro lado, podríamos incluso ignorar completamente los constructores dejando únicamente el que se genera por defecto y crear los objetos a partir de los métodos ‘get’ y ‘set’ tal que así:


	Date date = new Date();
	date.setYear(2013);
	date.setMonth(8);
	date.setDay(4);
	date.setHour(0);
	date.setMinute(0);
	date.setSecond(0);
	date.setMillisecond(0);

¿Qué desventajas suponen estas dos alternativas?

La primera tiene nombre propio y se conoce como ‘Telescoping constructor anti-pattern‘, sí, es una antipatrón, una de esas soluciones que a priori puede parecer buena pero que a la larga se vuelve en tu contra. Imagina que en lugar de siete propiedades tienes quince, o peor aún, piensa que esas quince propiedades no siguen ningún orden más o menos lógico y te toca ir creando tantos constructores como combinaciones posibles de propiedades, un infierno. Por otro lado, piensa que la sobrecarga en Java tiene ciertos límites y según que combinaciones de propiedades y tipos no podrás repetirlas más de una vez, por ejemplo:


	public Date(int year, int month, int day, int hour) {
		this(year, month, day, hour, 0, 0, 0);
	}

	public Date(int year, int month, int day, int minute) {
		this(year, month, day, 0, minute, 0, 0);
	}

	public Date(int year, int month, int day, int second) {
		this(year, month, day, 0, 0, second, 0);
	}

	public Date(int year, int month, int day, int milisecond) {
		this(year, month, day, 0, 0, 0, milisecond);
	}

Estos cuatros constructores no podrían coexistir al mismo tiempo, los cuatro reciben el mismo número y tipo de parámetros , el compilador no podría deducir cual intentas invocar.

La otra alternativa sería la típica estrategia que se aplica con los JavaBeans, como tal, hereda todas sus desventajas. Es muy común instanciar clases sin haber asignado todas las propiedades necesarias o con estados inválidos, el ejemplo más típico sería crear accidentalmente instancias de nuestra clase ‘Date’ con valores ‘0’ en año, mes o día porque hemos olvidado llamar al método ‘set’ que toca. ¿Quién no ha copiado / pegado estas asignaciones y ha generado algún ‘bug’ que luego ha estado molestando? Por otro lado, esta alternativa elimina la posibilidad de hacer la clase inmutable, algo tan común en nuestros querida clase ‘String‘ y que en según que situaciones puede venirnos muy bien.

¿Cómo solucionamos esta situación?

Para estos casos tenemos el ‘Builder pattern‘, vamos a verlo en acción con nuestra clase ‘Date’:


/**
 * Builder pattern.
 */
public class Date {

	// Todas las propiedades son 'final'
	// por lo tanto nuestro objeto es inmutable.
	private final int year;

	private final int month;

	private final int day;

	private final int hour;

	private final int minute;

	private final int second;

	private final int millisecond;

	// Creamos una clase interna 'Builder'
	public static class Builder {

		// Las propiedades obligatorias se declaran
		// 'final'
		private final int year;

		private final int month;

		private final int day;

		// Las propiedades opcionales se inicializan
		private int hour = 0;

		private int minute = 0;

		private int second = 0;

		private int millisecond = 0;

		// Pasaremos las propiedades obligatorias en el
		// constructor del 'Builder'
		public Builder(int year, int month, int day) {
			this.year = year;
			this.month = month;
			this.day = day;
		}

		// Crearemos un método público para cada una de
		// las propiedades opcionales, éstos se devolverán
		// a sí mismos ('this') para poder encadenar las
		// llamadas
		public Builder withHour(int hour) {
			this.hour = hour;
			return this;
		}

		public Builder withMinute(int minute) {
			this.minute = minute;
			return this;
		}

		public Builder withSecond(int second) {
			this.second = second;
			return this;
		}

		public Builder withMillisecond(int millisecond) {
			this.millisecond = millisecond;
			return this;
		}

		// Este método permitará crear el objeto 'Date'
		// resultante
		public Date build() {
			return new Date(this);
		}
	}

	// Sólo habrá un constructor privado para forzar
	// la creación de los objetos a partir del 'Builder'
	private Date(Builder builder) {
		this.year = builder.year;
		this.month = builder.month;
		this.day = builder.day;
		this.hour = builder.hour;
		this.minute = builder.minute;
		this.second = builder.second;
		this.millisecond = builder.millisecond;
	}

	// Omitimos los siete métodos 'get'
}

Veamos algunos ejemplos de como instanciar ahora esta clase:


	Date date01 = new Date.Builder(2013, 8, 4)
		.build();

	Date date02 = new Date.Builder(2013, 8, 4)
		.withHour(4)
		.build();

	Date date03 = new Date.Builder(2013, 8, 4)
		.withHour(5)
		.withMinute(15)
		.build();

	Date date04 = new Date.Builder(2013, 8, 4)
		.withHour(6)
		.withMinute(30)
		.withSecond(6)
		.build();

	Date date05 = new Date.Builder(2013, 8, 4)
		.withHour(6)
		.withMinute(30)
		.withSecond(6)
		.withMillisecond(300)
		.build();

Aspectos a tener en cuenta:

  • Nuestra código es ahora mucho más limpio y legible, los métodos públicos dentro del ‘Builder’ nos permiten dar mucha más expresividad a sus nombres.
  • La instanciación del objeto se lleva a cabo en una sola linea, de esta manera evitamos estados inconsistentes.
  • Podemos encadenar sin orden específico las asignaciones de las distintas propiedades opcionales mediante las llamada a los métodos oportunos, tantas como sean necesarias.
  • Siempre terminamos la instanciación del objeto con el método ‘build()’, éste se encarga de crear el objeto mediante el constructor privado.
  • Tenemos facilidad para hacer inmutable nuestra clase.
  • Es aconsejable aplicar este patrón como mínimo teniendo cuatro o cinco propiedades, escala muy bien y es fácil añadir nuevas propiedades, sobre todo opcionales.

Pero todo no podían ser ventajas, tener que crear un ‘Builder’ a mano para cada una de nuestras entidades puede llegar a ser bastante lento y tedioso, al fin y al cabo, nuestro IDE genera de forma automática la mayoría de código para constructores, ‘getters’ y ‘setters’, necesitamos una forma eficaz de aplicar este patrón que mostraré en la siguiente entrada.

Libros técnicos, una buena razón para comprar un e-Book

ebooks

Hace unos tres años empecé a desplazarme a diario en transporte público (Renfe cercanías) para ir a trabajar a Barcelona. Como actualmente vivo en Terrassa,  tardo aproximadamente una hora y cuarto desde que salgo de casa hasta que llego a mi trabajo. Debido a que hago dos trayectos diarios, ida y vuelta, tranquilamente dedico dos horas y media cada día sólo en desplazamientos. Como voy a trabajar cinco veces a la semana, de lunes a viernes, al final gasto entre doce y trece horas semanales sólo en trayectos.

Al principio los viajes se hacen llevaderos sin necesidad de entretenerte con nada en concreto, puedes distraerte con el paisaje, pensando un poco, viendo a la gente que sube y baja, etc. Con el tiempo los viajes se vuelven muy monótonos así que necesitas distraerte con algo.  Hay muchas posibilidades: dormir, escuchar música, ver alguna serie/película vía smartphone o tablet, leer, etc. Al final acabas haciendo un poco de todo, en mi caso, me decanté por la lectura.

Siendo como soy y dedicándome a lo que me dedico, empecé a comprar algún que otro libro técnico en papel para sacar provecho a esas horas de trayecto. Cuando consultas estos libros en su versión digital, por ejemplo en PDF, te alegras de que sean bien completos y tengan un buen número de páginas con grandes ejemplos de código, diagramas, esquemas de bases de datos, etc. El problema viene cuando ves el libro en papel y te encuentras con un tocho considerable que tienes que mover de un lado para el otro. Tener que llevarte una maleta sólo para poder transportar el libro que te estas leyendo no es muy práctico.

Head_First_Servlets_Jsp

Ejemplo práctico, mi mano y uno de estos libros

El problema se agrava debido a que la mayoría de libros técnicos de referencia son verdaderos tochos, no cuesta demasiado encontrar un libro de 600, 700 o más páginas de carácter técnico, hay de todo, eso esta claro. Lamentablemente no se suelen hacer versiones de bolsillo así que toca lidiar con su tamaño.

Debido a lo expuesto, me he decidido a comprar un e-Book. De un plumazo se solventan todos los problemas de movilidad, ya puede ser largo el libro, la ventaja de lo digital es evidente en este aspecto. Pero claro, no todo podían ser ventajas, resulta que no es lo mismo leer la versión e-Book de una novela donde mayormente sólo hay texto que un libro técnico donde hay multitud de ejemplos de código. Las novelas son fácilmente adaptables a los distintos formatos existentes para e-Book pero los libros técnicos pueden resultar una pesadilla para según que tamaños de pantalla y necesitan de una edición cuidadosa. Si bien puedes encontrar libros técnicos apropiadamente formateados en editoriales como Manning u O’Reilly, todavía no es del todo ‘habitual’ que sea así.

Y en esta situación me encuentro, leer la versión en PDF es horrible en dispositivos tipo Kindle Paperwhite o Sony PRS-T2, se hacen necesarias pantallas más grandes, tipo tablet. Precisamente es en tablets donde he leído buenas críticas a la hora de leer las versiones en PDF, lo que pasa es que no estoy dispuesto a comprar algo tipo iPad, iPad mini, Nexus 7 sólo para leer libros, me niego, por muchos otros usos que puedan tener. La diferencia de precio entre tablets y e-books no me convence.

En definitiva, pese a las claras desventajas que puedes encontrarte en las ediciones digitales de los libros técnicos, el tema de la movilidad tiene un mayor peso para mí, sólo por esto me decantaré por un e-Book, aunque no sé todavía cual, tengo que acabar de decidirme y seguramente escribiré alguna entrada más sobre estos temas.

¿Qué opináis vosotros?