Objetos valor

Vaya, he estado ojeando los borradores acumulados en mi cuenta de Blogger y parece que por aquí había una idea que no estaba mal. No se porque razón no completé este artículo y lo publique, pero como nunca es tarde si la dicha es buena, allá vamos.

Hace poco (bueno, ahora ya hace más de año y medio :-) leí un artículo de Krzysztof Adamczyk sobre la potencia de hacer uso de objetos valor en nuestro código, y he pensado que podría ser interesante que presente mi propia opinión al respecto de este tema. Para ello tomaré prestadas las ideas del propio Krzysztof Adamczyk y las expuestas por Dan Berg Johnsson en su presentación en la QCon London 2009.

*Nota al lector*

El siguiente código no es de producción, tan solo es a modo de ejemplo. Está realizado en Scala, pero cualquier persona con conocimientos de Java lo puede entender de forma sencilla.

¿Qué es un objeto valor?

Lo mejor será empezar por el principio. Para todos aquellos que no sepan lo que es un objeto valor, Eric Evans define en su libro un objeto valor de la siguiente manera.
"An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object."
He preferido dejarlo en el idioma original porque es posible que yo metiese la pata al hacer la traducción, por lo que con esta definición y lo que cada uno entienda de ella, empezaré con el contenido de esta entrada.

Los detalles del dominio se hacen explícitos

Una de las ventajas de hacer uso de los objetos valor, es que en el código desaparecen los tipos como String o int por tipos como PhoneNumber o Email. Aquí muchos pensaréis que esto no es ninguna ventaja sino que añade complejidad, puede ser pero dejadme unos párrafos de ventaja por favor.

Imaginad que estamos trabajando en un nuevo y avanzadísimo sistema de gestión ... como por ejemplo, una simple una agenda. Como en cualquier agenda, tendremos un objeto de modelo que define el contacto y generalmente nos encontraríamos con algo similar al siguiente código.

class Contact(val firstName: String, val lastName: String,
val phoneNumber: String) {

override def toString(): String = {
firstName + " " + lastName + " : " + phoneNumber
}
}

object Agenda {

def main(args: Array[String]) = {
val contact = new Contact("Pepe", "García", "922222222")
println(contact)
}

}

Genial, muy sencillo, pero ahora imaginemos  que nuestro cliente quiere que su agenda indique de forma automática con un icono (que nosotros representaremos en el ejemplo con texto) cuando se trata de un teléfono fijo y cuando de un móvil, por poner un ejemplo. Muchas veces la opción más utilizada es la siguiente.

class Contact(val firstName: String, val lastName: String,
val phoneNumber: String) {

def phoneNumberType(): String = {
if (phoneNumber.startsWith("6"))
"móvil"
else
"fijo"
}

override def toString(): String = {
firstName + " " + lastName + " : " + phoneNumber +
" (" + phoneNumberType + ")"
}
}

object Agenda {

def main(args: Array[String]) = {
val pepe = new Contact("Pepe", "García", "922222222")
println(pepe)
val juan = new Contact("Juan", "Juanez", "633445566")
println(juan)

}

}

Pero ¿qué ocurriría si hiciésemos esto otro?.

class PhoneNumber(val phoneNumber: String) {

def withType(): String = {
phoneNumber + " (" + phoneType + ")"
}

private def phoneType(): String = {
if (phoneNumber.startsWith("6"))
"móvil"
else
"fijo"
}

}

class Contact(val firstName: String, val lastName: String,
val phoneNumber: PhoneNumber) {

override def toString(): String = {
firstName + " " + lastName + " : " + phoneNumber.withType
}

}

object Agenda {

def main(args: Array[String]) = {
val pepePhone = new PhoneNumber("922222222")
val pepe = new Contact("Pepe", "García", pepePhone)
println(pepe)
val juanPhone = new PhoneNumber("633445566")
val juan = new Contact("Juan", "Juanez", juanPhone)
println(juan)

}

}

Ciertamente son bastantes más líneas de código, pero ¿qué hay de la limpieza? y aún más importante, de esta manera se cumple la regla que dice que cada elemento debe tener una única responsabilidad. Ahora la clase Contact no tiene lógica sobre la forma en la que se muestra el número de teléfono.

Al tratarse de un ejemplo hemos metido lógica de la vista en el modelo, pero en un caso real lo que haríamos es tener un método en PhoneNumber que nos indique de que tipo de dispositivo se trata, y ya se encargará la capa de la vista de representarlo.

El código se extiende más fácilmente

Sigue sin parecerte nada especial y la legibilidad del código no te importa mucho, aunque debería. Pues otra ventaja es que el código se extiende más fácilmente y de manera comprensible. Imagina que ahora queremos saber qué números de teléfono tienen errores, pues basta con que añadamos dicha funcionalidad en el propio objeto PhoneNumber, sin seguir complicando otras clases ni desperdigar la lógica por el resto de la aplicación.

Para acabar me gustaría señalar como de esta manera hemos evitado tener un modelo anémico. Es muy usual que se creen modelos como si fueran simples CRUD y luego se reparta cierta lógica en validadores y helpers, ¿no es más lógico que sea el propio objeto el que tenga la lógica que le afecta únicamente a él?



Retrospectivas: "An elegant solution for keeping track of reality"


El otro día estaba viendo Origen por enésima vez (sí me gusta mucho mucho, ¿y qué? :-), y una de las frases del personaje de Ellen Page me hizo recapacitar.

Muchas personas habrán visto ya la película de Christopher Nolan y sabrán a lo que llaman totem en la misma. Para aquellos que no la hayan visto y resumiendo lo suficiente para no estropear la trama, un totem es un objeto trucado en el que solo su dueño conoce su "comportamiento", de esta manera utilizando dicho objeto puede comprobar fácilmente si está soñando o despierto. Por ejemplo, el protagonista tiene una peonza que es capaz de girar indefinidamente cuando está dentro de un sueño, de forma que si se para es la realidad y si sigue girando es un sueño.


Cuando a Ariadne le enseñan la utilidad de los totem, y ella misma se fabrica uno, utiliza una frase muy interesante, "an elegant solution for keeping track of reality". Pues en el desarrollo de software también tenemos un totem con esta funcionalidad, en forma de retrospectivas.

Pero en la vida real, en un proyecto, estamos trabajando no es una película, no estamos soñando, ¿o sí?. Desde luego no es un sueño, pero en un proyecto sí tenemos que disponer de "un totem" que nos permita saber si estamos haciendo las cosas bien o no, esto nos permitirá corregir el rumbo a tiempo, por seguir el símil con la película, despertarnos.

Personalmente creo que una retrospectiva tiene una serie de características comunes con un totem.

  • Cada persona tiene su propio totem del que conoce su comportamiento, pues cada equipo debe tener su propia forma de llevar una retrospectiva. Hay múltiples formas de hacerlo y es normal al principio copiar las prácticas de otros, pero con el tiempo se deben ir adaptando a las características específicas de cada equipo.
  • De nada sirve tener un totem si nunca lo utilizas, pues lo mismo aplica a las retrospectivas. Hay que utilizar retrospectivas con cierta "frecuencia" para detectar las posibles mejoras y tomar medidas adecuadas, sino será como si estuviéramos en un sueño y no siempre tiene porque acabar bien :-)
  • Utilizar el totem no implica que te despiertes, debes tomar tus propias medidas al respecto, igualmente el resultado de una retrospectiva debe tener una serie de acciones a tomar o simplemente será otra reunión más.

Quizás si no tienes mucha experiencia con retrospectivas te interesen los siguientes enlaces, para saber por donde empezar.


Por lo tanto, da igual la metodología, el tamaño del equipo o el tipo de proyecto, siempre puedes hacer uso de retrospectivas para saber si estás soñando o estás despierto.

No es necesario aplicar tus nuevos conocimientos al instante


Tenía pensada una entrada un poco más larga sobre el ciclo de vida de Hibernate, pero al finalizar la reunión de AgileCanarias de este viernes entre caña y caña, surgió un tema muy interesante que me gustaría comentar en el blog.

Hay un comportamiento típico en la mayoría de desarrolladores (de software) que conozco, incluyéndome a mi mismo, que es la tendencia a utilizar todo lo que aprenden a la mínima oportunidad. Y esto está bien cuando no se trata de proyectos reales que están en producción. Por ahí se suele decir que si "tu única herramienta es un martillo, todos tus problemas se parecerán sospechosamente a un clavo", pues este es un caso bastante similar pero en la otra cara de la moneda. Si te compras un taladro, estarás como loco buscando donde hacer un agujero, cuando lo que necesitas es apretar un tornillo.

Voy a dar una pequeña lista de razones por las que se debe andar con mucho ojo antes de aplicar una nueva técnica, herramienta, metodología o similar.

  • Cuando utilizas nuevas aproximaciones que acabas de aprender, generalmente no conoces todas sus implicaciones. Siempre es mejor probar en un pequeño toy project (ver Apprenticeship Patterns) o hacer un spike, antes de usar dichas aproximaciones en "la vida real".
  • Como continuación del punto anterior. Las nuevas "herramientas" siempre vienen con su "manual de instrucciones", que deberíamos al menos ojear aunque hayamos visto como la utilizan otras personas, porque puede que la estemos utilizando incorrectamente.
  • Lo normal es buscar entre todas nuestras herramientas cual es la adecuada para hacer un trabajo, no buscar cuál es el problema adecuado para utilizar una determinada herramienta. Si lo hacemos de la segunda manera por norma general llegaremos a lo que se suele denominar sobre-ingeniería y tendremos código que al final nunca se utiliza. En el peor de los casos tendremos una solución que no es la adecuada, no escala bien o no cumple su objetivo con eficacia.

Yo creo que esto sucede porque asoma el pequeño ingeniero/científico, o como lo quieras llamar, que llevamos dentro. A veces necesitamos algo nuevo que nos llame la atención, que nos entretenga, que despierte de nuevo nuestro interés innovador. Pero por el bien de nuestro yo futuro, será mejor hacer algunas pruebas iniciales en un toy project, y pensar con la cabeza fría cuales son las mejores herramientas de las que disponemos para nuestros problemas actuales.

Si es algo que te ha pasado alguna vez, te sientes identificado, piensas que es una tontería o que simplemente este artículo es una chorrada, no dudes en dejar tus comentarios :-D