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.

EqualsBuilder & HashCodeBuilder, mejorando lo visto

Siguiendo lo comentado en la entrada anterior, cuando nuestras clases empiezan a acumular un buen número de propiedades, generar de forma automática el método ‘equals()’ engendra un monstruo.

Si bien podemos refactorizar e intentar maquillar el asunto, lo más probable es que caigamos en lo que se conoce como un antipatrón, y más concretamente acabaremos reinventando la rueda cuadrada. ¿Qué significa esto? Pues que gastaremos tiempo en hacer algo que ya se ha hecho con anterioridad y seguramente nuestra solución sea ‘peor’ que la que ya existe.

Esta situación ocurre a diario, cuando estamos aprendiendo nos viene bien para ejercitar un poco el coco pero cuando tenemos plazos de entrega, hay que ser práctico. En muchas ocasiones me sorprendo a mi mismo uniendo piezas ajenas para llevar a cabo pequeñas funcionalidades, esto no es malo mientras entiendas lo que estas haciendo, de lo contrario la cosa puede degenerar en una variante de la programación basada en el copiar/pegar (Otro antipatrón).

Pero no nos desviemos del tema, necesitamos encontrar una solución para nuestro problema con los ‘equals()’. Las preguntas clave que hay que hacerse son:

  1. ¿Soy el primero en enfrentarme a este problema? (Probablemente no)
  2. ¿Habrá alguna solución ya existente para este problema? (Probablemente sí)

Es aquí donde entra en juego la fundación Apache y sus archiconocidas librerías Apache Commons, sin entrar en grandes detalles, estas librerías te harán la vida más fácil como programador java, poco a poco iremos examinándolas con más detalle.

Imagen

Concretamente para este caso, las clases EqualsBuilder y HashCodeBuilder de la librería Apache Commons Lang cubren nuestras necesidades. Vamos a verlas en acción, comencemos por el método ‘equals()’ de la clase ‘Persona’ utilizada en la entrada anterior:


	@Override
	public boolean equals(Object obj) {

		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		if (!(obj instanceof Persona)) {
			return false;
		}

		Persona other = (Persona) obj;

		EqualsBuilder eBuilder = new EqualsBuilder();

		eBuilder = eBuilder.append(this.dni, other.dni);
		eBuilder = eBuilder.append(this.edad, other.edad);
		eBuilder = eBuilder.append(this.nombre, other.nombre);

		return eBuilder.isEquals();
	}

La mejora en cuanto a legibilidad y mantenibilidad es muy notable, veamos si sucede igual con el método ‘hashCode()’:


	@Override
	public int hashCode() {

		// Creamos una instancia, el primer valor es para la
		// inicialización, el segundo el número primo para las
		// multiplicaciones
		HashCodeBuilder hcBuilder = new HashCodeBuilder(17, 31);

		hcBuilder.append(this.dni);
		hcBuilder.append(this.edad);
		hcBuilder.append(this.nombre);

		return hcBuilder.toHashCode();
	}

La respuesta es afirmativa, el código se simplifica a costa de añadir una libería (‘jar’) a nuestro proyecto. Además podemos estar tranquilos en lo que se refiere a su implementación, el método ‘hashCode()’ esta hecho siguiendo las directrices de Effective Java (Igual este libro es interesante :)).

No he comentado nada en relación a como añadir la librería, todo depende de como gestionemos las dependencias en nuestro proyecto, para este ejemplo no me he complicado en exceso y he añadido el ‘jar’ de la librería (‘commons-lang3-3.1.jar’) al Classpath del proyecto, con Maven añadiríamos la dependencia en el POM de nuestro proyecto (Un tema interesante para una futura entrada).