Archivo | java RSS for this section

Enlaces de los martes (16 oct 2012)

Otro martes más con unos cuantos enlaces interesantes. Esta semana he recopilado temáticas muy diversas, desde temas técnicos (excepciones), hasta temas familiares (geek dads):

Refactorizacion: inserta un método ajeno

En este post voy a describir una de las muchas refactorizaciones que describe Martin Fowler en su libro Refactoring: improving the design of existing code (mis impresiones sobre el libro). He escogido la refactorización de insertar método ajeno por ser una de las que, aun siendo sencilla, no había sido consciente de utilizarla hasta leer el libro.

Descripción

La refactorización consiste en crear un método en una clase (clase cliente) cuando en realidad ese método debería pertenecer a otra clase (clase servidor). De ahí el nombre de método ajeno (traducción libre de foreign method)

Por qué es necesaria

Esta refactorización aparece ante la necesidad de crear un método en la clase servidor, pero es imposible cambiar el código fuente de la misma.

Cómo llevar a cabo la refactorización

Lo primero es crear un método en la clase cliente que haga lo que necesitamos que haga la clase servidora. El primer parámetro de este método será un objeto de la clase servidora. En caso de necesitar algún parámetro más, éstos serían los parámetros en caso de que el método existiera en la clase servidora.

El objetivo de crear un nuevo método es el de poder reutilizar el código y evitar duplicidades. De esta forma podremos retutilizar el código del nuevo método, y en el caso de que en un futuro podamos cambiar el código fuente de la clase servidora, ese cambio será menos doloroso.

Martin Fowler aconseja comentar el método como `método ajeno`, para poder encontrar métodos candiatos a mover a sus clases correspondientes. En mi opinión, no creo que esto ayude demasiado a la hora de mantener el código, pero tampoco lo veo perjudicial. Así que aquí, que cada cual siga sus preferencias.

Código de ejemplo

Partimos del siguiente código de ejemplo, es un ejemplo muy sencillo pero espero que ilustre el caso lo suficientemente bien como para comprender la refactorización:

// ...
Date nextDay = new Date(previousDay.getYear(), 
                        previousDay.getMonth(), 
                        previousDay.getDate() + 1);

Como vemos, nuestro código necesita varios datos de la clase servidora (Date, objeto previousDay). Si la clase Date tuviera un método nextDay, o similar, nuestro código quedaría realmente simple. Pero no lo tiene, y lo pero de todo es que tampoco tenemos la posibilidad de modificar Date para incorporarle ese método.

Está bien, creemos pues nuestro método ajeno:

Date nextDay = nextDay(previousDay);
// ...
// foreign method
private Date nextDay(Date aDay) {
    return new Date(aDay.getYear(), 
                    aDay.getMonth(), 
                    aDay.getDate() + 1);
}

Sí, el código es prácticamente el mismo, pero ahora tenemos un método, la principal forma de reutilizar código. En el caso de poder modificar la clase Date, sería trivial mover este método a esa clase. De hecho, mover método, es una de las primeras refactorizaciones (y más básicas) explicadas por Martin Fowler en Refactoring.

Refactoring: improving the design of existing code

Refactoring: improving the design of existing code

Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts

Por qué lo he leído

El título me pareció sugerente. Me gusta que el código que utilizo, el código que escribo o el código que leo, tenga muy buena apariencia, sea legible, sencillo y, por qué no, sea bonito.

No soporto ver clases mal identadas, o con nombres que no significan nada. Lo odio. Y siempre he pensado que refactorizar el código ayuda a conseguir todo esto.

Qué esperaba

Evidentemente, según el título, esperaba un manual, una guía, sobre cómo refactorizar. Esperaba que el libro me enseñara a refactorizar, a mejorar las refactorizaciones que hago en mi día a día.

Teniendo a Martin Fowler como autor principal, esperaba una descripción de experiencias, una demostración de veteranía, de la que poder beneficiarme y aprovechar esa sabiduría para mejorar en mi trabajo.

Qué encontré

Encontré un catálogo de acciones de refactorización. Muchas de las refactorizaciones descritas en el libro son sencillas, otras ya las practicaba sin conocerlas formalmente y otras estoy seguro de que me resultarán muy útiles de aquí en adelante.

Conclusiones

El libro me ha resultado un poco denso, y a veces muy lento, incluso llegó un momento en el que estuve a punto de dejar de leer.

Cada acción de refactorizar está muy detallada. Esto hace que leerlo sea un poco aburrido, pero como referencia no tiene precio.

Como ya he dicho antes, muchas refactorizaciones ya las estaba realizando personalmente, pero ni las había categorizado, ni les había puesto nombre. Por esto mismo, no he encontrado muchas refactorizaciones desconocidas para mí, pero al mismo tiempo me ha permitido categorizar, catalogar, varias refactorizaciones que estaba realizando. De las refactorizaciones nuevas, tengo pensado escribir sobre alguna de ellas.

Sin duda, es un libro a tener en cuenta para cualquier desarrollador software, tanto como para su lectura como para tenerlo de referencia. Totalmente recomendado.

Pasajes que quiero recordar de este libro

El primer paso, es construir un conjunto sólido de tests para el código que se va a refactorizar.

En la mayoría de los casos, un método debería residir donde los datos que el método usa.

Para refactorizar, lo mejor es pasito a pasito, lo que lleva a cometer menos errores.

La lección más importante del ejemplo es el ritmo del refactor: test, cambio pequeño, test, cambio pequeño, test, …

¿Por qué refactorizar? Mejora el diseño del software, hace el software más fácil de mantener, ayuda a encontrar errores, ayuda a programar más rápido.

¿Cuándo no refactorizar? Cuando es necesario empezar desde cero, o cuando estás cerca de una entrega.

Cuando los testers encuentran un bug, hay que hacer dos cosas: arreglarlo y crear un test que te asegure de que el bug no volverá a aparecer.

El estilo que yo sigo es el de fijarme en todas las cosas que la clase puede hacer y pruebo cada una de ellas para todas las condiciones que pueden hacer que la clase falle. No es lo mismo que testear cada uno de los métodos públicos. Los tests deben de exponer cierto riesgo.

La repetición es la raiz de todos los males del software.

(Las sentencias de protección indican: esto es raro, haz algo y sal de aquí) La sentencia de protección o retorna de la función o lanza una excepción, nunca debería dejar seguir ejecutando el método.

A veces, se da por supuesto algo y se escribe un comentario. Es mejor hacer explícita lo asumido creando una sencia assert.

Mi experiencia me dice que conforme refactorizar se va convirtiendo en una rutina, deja de ser una sobrecarga y comienza a ser algo esencial

The Clean Coder

The Clean Coder: a code of conduct for professional programmers

Rober C. Martin

Por qué lo he leído


Me decidí a leer este libro porque leyendo varios blogs en español encontré más de una referencia, por lo que parecía un buen libro para leer. Además, conseguí que un amigo me dejara un ejemplar. Todos los requisitos cumplidos.

Qué esperaba

La verdad es que el propio título ya dice bastante: “clean coder”. Esperaba encontrar una guía de buenas prácticas para aplicar en mi día a día profesional, una serie de consejos sobre cómo desarrollar software de calidad, limpio, que sea fácil de leer. Estaba incluso decidido a soportar una lista de tópicos sobre el desarrollo del software, pero estaba seguro de que iba a encontrar consejos/prácticas nuevas y, por supuesto, iba a aprender técnicas útiles.

Qué encontré

La verdad es que no encontré exactamente lo que buscaba. Robert C. Martin nos cuenta más batallitas que otra cosa, pero no nos miente, desde el principio del libro nos advierte que éste es un libro de experiencias personales.

El libro es un conjunto de experiencias, unas buenas y otras malas, del autor, de las cuales, Uncle Bob ha aprendido todo lo que sabe.

El libro también es un conjunto de consejos, una guía, de cómo Robert cree que se debe comportar un desarrollador de software profesional.

Conclusiones

El libro me ha encantado.

El tono que usa Uncle Bob para relatar sus experiencias es informal y cercano. En algunas de ellas puedo ver reflejados a compañeros míos o incluso a mí mismo (aunque ya no trabajamos con tarjetas perforadas, pero la esencia sigue siendo la misma).

Algunos consejos son, para mi gusto, un poco extremistas, pero el autor da sus razones de porqué él cree que debe ser así. Al fin y al cabo, han sido y son sus vivencias y no las impone a nadie. Con otros consejos no estoy muy de acuerdo, pero siempre es bueno conocer otras opiniones.

En definitiva, si te preocupa tu trabajo, éste es un buen libro para conocer qué piensa una de las personas más importantes en el mundo del desarrollo del software. Así es como Robert C. Martin cree que debe ser un profesional:

  • Es responsable: cuando un profesional comete un fallo, arregla el desorden que ha provocado.
  • Usa su conocimiento para construir, no para destruir.
  • Debe estar seguro de que funciona (tests).
  • Conoce el campo en el que trabaja (banca, transporte, …).
  • Nunca ridiculiza a los demás.
  • Trabaja duro para encontrar caminos creativos para decir “si”, para entregar funcionalidad.
  • La actitud que tiene hacia las interrupciones es de mostrarse voluntarios de poder ser de ayuda. Se ofrece a ayudar a los compañeros si ven que están teniendo problemas.
  • Programa en parejas.
  • Mantiene la mente abierta a nuevas tecnologías, ideas, diseños, y acepta que estas novedades pueden ser incluso mejor que las actuales.

Pasajes que quiero recordar de este libro

Debes ser responsable de tus imperfecciones, lo primero que debes aprender es a pedir perdón, después debes hacer que tu ratio de errores tienda a cero.

Entregar funcionalidad a coste de romper la arquitectura es un error de principiante. Comprometer la arquitectura es comprometer el futuro.

Regla del Boy Scout: deja siempre el código un poquito más limpio de lo que te lo encontraste.

Tu carrera es tu responsabilidad, no culpes a tu empresa.

La mejor forma de aprender, es enseñando. Otra forma de aprender es colaborar con otra gente.

Los problemas de tu empresa (quien te paga) son tus problemas.

Programar es un acto de creación. Cuando escribes código, estás creando algo de la nada.

(Sobre estimaciones y compromisos) Negocia para alcanzar el máximo de las dos partes enfrentadas.

La promesa de “lo intentaré” es la aceptación de que guardas un esfuerzo extra, y además es un compromiso de que necesitarás aplicar ese esfuerzo extra para conseguir el objetivo.

Agresión pasiva: dejar que tu compañero falle públicamente cuando sabes de antemano que lo que está haciendo fallará.

Dilo, comprométete y hazlo. (no lo intentes, hazlo)

Programar es una actividad agotadora, que reta tu inteligencia […], si estás cansado o distraído, no programes.

Las estimaciones tienen que estar basadas en datos, no incorpores esperanza en tus estimaciones. Y no dejes que los demás tengas esas esperanzas.

No aceptes hacer horas extras, excepto si: te lo puedes permitir personalmente, es para un período corto y tu jefe tiene un plan B por si no funciona.

La única forma de mejorar la planificación, es reducir el alcance. No caigas en la tentación de correr.

Los tests te dan la certeza suficiente para entregar.

TDD (desarrollo dirigido por tests) es una disciplina que mejora la certeza, el coraje, reduce los defectos, la documentación y mejora el diseño.

Katas:

Tom DeMarco: Una ambigüedad en los requisitos, refleja un conflicto en el cliente.

Los tests unitarios y de aceptación, primero son documentos y luego tests.

Nada tiene un efecto tan negativo a largo plazo en la productividad de un equipo desarrollador como la deuda técnica.

Programar conlleva trabajar con gente. Si quieres pasar tus días desarrollando software, deberás aprender a hablar con la gente.

Lleva tiempo que un equipo funcione, por eso, las empresas profesionales, organizan proyectos alrededor de equipos que funcionan, nunca forman equipos alrededor de proyectos nuevos.

Lista de logros para ser mejor programador

Puede ver una versión activa de este post en mi nuevo blog. He decidido dejar de publicar en este blog por éstas razones. Su filosofía es la misma que éste, pero espero no tener los problemas que me he encontrado aquí.

Me he permitido copiar el título del post del blog donde encontré la idea para escribir el mío propio: Lista de logros para ser mejor programador (que a su vez fue inspirado por Jason Rudolph) y espero que la palabra se extienda y poco a poco se cree toda una marea de programadores que queremos mejorar.

La idea original es hacer públicos una serie de logros que se quieren conseguir como programador. Yo he hecho lo propio y he comenzado haciendo un fork del gist original de Jason: https://gist.github.com/1189847

Me gusta mucho la idea de exponer, de publicar las metas que uno quiere conseguir. Eso hace que te esfuerces más por llegar a ellas, por hacerlas realidad. Espero que hacerlas públicas me ayude, y también que inspire a otros profesionales del desarrollo del software para tomar iniciativas similares.

Yo en particular no estoy de acuerdo con todas las metas de Jason, por lo que iré modificando mi gist con mis propias metas. Puede que no sean tan ambiciosas como las de Jason, puede que sean menos concretas. Pero serán las mías.

Como bonus, me gustaría compartir un enlace relacionado con el aprendizaje perpetuo. En Tu no sabes programar encontré varios de las metas con las que empezar a personalizar mi gist de logros para ser mejor desarrollador: patrones y principios de diseño, metodologías y disciplinas de desarrollo, herramientas, …

Enlaces relacionados:

¿Qué es un gist?

Traducción libre de la página de gists de github:
Gist es una forma sencilla de compartir con otros pequeños archivos de ejemplo. Todos los gists son repositorios git, por lo que son automáticamente versionados, pueden ser duplicados y usados como un repositorio git.

Uso de JPA, Hibernate y Derby

Puede ver una versión activa de este post en mi nuevo blog. He decidido dejar de publicar en este blog por éstas razones. Su filosofía es la misma que éste, pero espero no tener los problemas que me he encontrado aquí.

Tengo una pequeña aplicación en la que uso Apache Derby como base de datos para guardar los datos.

Hasta ahora, estaba utilizando el patrón DAO para guardar los datos que quería hacer persistentes en la aplicación. Cada clase DAO se encargaba de crear, borrar, editar y actualizar un tipo de datos. Estas operaciones las hacía mediante SQL puro y duro. No es que el modelo de datos que utilizo sea muy complicado, pero es tedioso editar consultas SQL para cada tipo de dato que quieres persistir.

Así pues, decidí que debería utilizar algo un poco más elaborado. Java dispone de la Java Persistence API, así que, ¿porqué no usarla? Y este post describe los primeros pasos a dar para utilizar JPA en una aplicación.

JPA es una definición, por sí sola no hace nada, necesita de una implementación para realizar realmente el trabajo. Existen varias implementaciones (ver comparativa). De todas ellas he elegido Hibernate. Puede que no sea la mejor, puede que no sea la más rápida o la más eficiente, pero creo que es la más conocida y la referencia para el resto de implementaciones.

El ejemplo, paso a paso

Nuestro ejemplo va a consistir en algo muy (pero que muy) sencillo, pero que nos va a permitir aprender cómo configurar hibernate con nuestra base de datos derby. Nuestros datos a guardar van a ser objetos de la clase Person, que va a tener un nombre. No va a haber relaciones con ningún otro objeto, así que sólo exisitirá una tabla en la base de datos. Más sencillo, imposible.

Datos que serán persistentes

Como ya hemos visto, sólo vamos a persistir objetos de la clase Person, la cual tendrá un campo id, que funcionará como la clave principal de la tabla, y un campo name, el nombre de la persona. A continuación vemos el código de esta clase:

package es.rct.jpa.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private long id;
    private String name;

    public void setName(final String name) {this.name = name;}
    public String getName() {return name;}
    public void setId(final long id) {this.id = id;}
    public long getId() {return id;}
}

De esta clase vamos a destacar 3 anotaciones, pertenecientes a JPA. Las podemos encontrar en el paquete javax.persistence:

  • Entity: indica que esta clase es una entidad, lo cual significa que esta clase tiene correspondencia con una tabla en la base de datos.
  • Id: indica qué campo de la clase va a ser utilizado como clave primaria de la tabla representada por la entidad.
  • GeneratedValue: indica que la clave va a ser generada automáticamente por el motor de persistencia (hibernate)

Dependencias de Maven

Para poder usar Derby como base de datos y Hibernate como implementación de JPA, debemos incluir las siguientes dependencias en nuestro fichero pom.xml de configuración de maven:

<!-- ... -->
<dependency>
	<groupId>org.apache.derby</groupId>
	<artifactId>derby</artifactId>
	<version>10.6.2.1</version>
	<type>jar</type>
	<scope>compile</scope>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>3.4.0.GA</version>
	<type>jar</type>
	<scope>compile</scope>
</dependency>
<!-- ... -->

Las versiones de las dependencias pueden variar. Aquí aparecen las que yo personalmente he utilizado.

Configurar JPA / Hibernate / Derby

Para configurar JPA, debemos escribir la configuracion en un fichero llamado persistence.xml, en un directory META-INF, accesible desde el directorio de trabajo de nuestra aplicación. Yo lo he creado en src/main/resources/META-INF/persistence.xml, ya que maven empaqueta el contenido del directorio src/main/resources en el jar de la aplicación, con lo que tenemos el resultado deseado.

El contenido del fichero de configuración JPA es el siguiente:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> 
	<persistence-unit name="test-jpa" transaction-type="RESOURCE_LOCAL">

		<provider>org.hibernate.ejb.HibernatePersistence</provider>

		<class>es.rct.jpa.model.Person</class>
		<exclude-unlisted-classes>true</exclude-unlisted-classes>

		<properties>
			<property name="hibernate.connection.url" 
					value="jdbc:derby:memory:testing-jpa;create=true" />
			<property name="hibernate.connection.driver_class" 
					value="org.apache.derby.jdbc.EmbeddedDriver" />
			<property name="hibernate.dialect" 
					value="org.hibernate.dialect.DerbyDialect" />
			<property name="hibernate.hbm2ddl.auto" value="create" />
			<property name="hibernate.connection.username" value="" />
			<property name="hibernate.connection.password" value="" />
		</properties>

	</persistence-unit>
</persistence>

De este fichero xml podemos destacar las siguientes etiquetas:

  • persistence-unit, atributo name: define una unidad de persistencia, es obligatorio darle un nombre, para poder crear un EntityManager en nuestra aplicación. El EntityManager es el encargado de manejar la persistencia de nuestros datos.
  • provider: aquí indicamos que queremos usar Hibernate como implementación de JPA
  • class: debe existir una etiqueta class por cada clase que queramos persistir, es decir, una por cada entidad que formará nuestra unidad de persistencia.
  • Dentro de las etiquetas properties, definimos propiedades propietarias de la implementación JPA. De entre ellas cabe destacar:
  1. hibernate.connection.url: define la URL de conexión a la base de datos. Aquí estamos configurando Derby como base de datos. Estamos creando una base de datos llamada “testing-jpa” en memoria (no en disco).
  2. hibernate.dialect: configuramos Hibernate para que “hable” con Derby

Crear un test para probar el funcionamiento

Ahora sólo queda crear nuestro código para almacenar algunos objetos del tipo Person.

Primero, debemos crear un objeto EntityManager, que manejará todo lo relacionado con la persistencia: transacciones, guardar datos, actualizarlos, borrarlos, etc.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-jpa");
EntityManager em = emf.createEntityManager();

Una vez terminemos de utilizar nuestro EntityManager, debemos cerrarlo:

em.close();
emf.close();

Ahora ya disponemos de un objeto “em” para poder persistir objetos de tipo Person:

private void insertSomeData() {
    Person p = new Person();
    p.setName("person 01");
    Person p1 = new Person();
    p1.setName("person 02");

    em.getTransaction().begin();
    em.persist(p);
    em.persist(p1);
    em.getTransaction().commit();
}

Para poder guardar los datos en base de datos, debemos arrancar una transacción, llamar al método “persist” y terminar la transacción. Si queremos indicar que ha habido un error durante la transacción, y no queremos llevarla a cabo, llamaríamos al método “rollback” en lugar del método “commit”.

Para comprobar que realmente hemos almacenado los objetos en la base de datos, sólo tenemos que buscarlos por identificador. Ya que sólo hemos almacenado 2 objetos, y son los 2 primeros, los ids serán 1 y 2 respectivamente:

private void listInsertedData() {
    em.getTransaction().begin();
    for (long i = 1; i <= 2; i++) {
        Person pFinded = em.find(Person.class, new Long(i));
        System.out.println("Id: " + i + ", name: " + pFinded.getName());
    }
    em.getTransaction().commit();
}

Código fuente del ejemplo

Puedes descargar/ver el código fuente en este repositorio de github: https://github.com/rchavarria/JPAHibernateDerby

Referencias

Si quieres profundizar en el tema, aquí dejo unos enlaces que me han sido de gran ayuda.

  1. Excelente tutorial de JPA
  2. Configuración de Derby para trabajar en memoria
  3. Configuración de JPA para usar Hibernate
  4. Hibernate en entornos JavaSE
  5. Comparativa de implementaciones de JPA

Heisenbugs

¡Bien! Ya tengo mi nueva funcionalidad implementada. La aplicación va a ser de lo mejorcito con lo que acabo de desarrollar. Llevo depurando este código toda la tarde. ¿Qué puede fallar? He recorrido instrucción por instrucción y todo se ejecuta según lo previsto.

Lanzo la aplicación …

¡WTF! ¡Un null pointer! ¡No puede ser! si lo he depurado miles de veces.

¿Nunca os habéis encontrado con un error de este tipo?

Yo muchas veces, pero no se me había ocurrido ponerle nombre. Hace poco, mientras leía Java concurrency in practice de Brian Goetz (y otros autores) encontré el nombre, y me gustó tanto que decidí escribir sobre ello. Mucho tiempo después aquí está la entrada.

El nombre en cuestión es heisenbug. Me gustó mucho porque mezcla con sentido del humor los conceptos de bug (estoy hablando de un error en una aplicación, ¿recuerdas?) y Heisenberg. La mezcla de este segundo concepto es lo que me hizo gracia: se refiere a un bug que aparentemente no somos capaces de reproducirlo, por lo que no podemos conocer dónde se está produciendo, es decir, su posición. Es similar al principio de incertidumbre de Werner Heisenberg, que en palabras llanas dice:

No se puede determinar, en términos de la física clásica, simultáneamente y con precisión arbitraria, ciertos pares de variables físicas, como son, por ejemplo, la posición y el momento lineal de una partícula (digamos un electrón).

En realidad, el problema siempre ha estado ahí, pero no se dieron las condiciones adecuadas para que el error apareciera, es decir, no hemos sido capaces de conocer su posición.

No viene a cuento, pero Microsiervos lleva una temporada publicando términos que podrían incluirse en un geekcionario, entre los cuales podría encajar heisenbug.

Nota: Los autores de Java concurrency in practice son: Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea.