Archive | enero 2012

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

TibrvException[error=901,message=Library not found: tibrvj]

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í.

Actualmente, en el trabajo estamos utilizando TIBCO Rendezvous como herramienta de mensajería entre nuestras aplicaciones java, y recientemente me he encontrado con un problema. En este post voy a hablar de dicho problema, así que si no te interesa el tema, ya has terminado de leer el artículo 🙂

Problema

El problema es el siguiente: no soy capaz de que nuestras aplicaciones carguen la librería para poder comunicarse. Más concretamente, estoy obteniendo la siguiente excepción:

[...]
Caused by: TibrvException[error=901,message=Library not found: tibrvj]
    at com.tibco.tibrv.Tibrv.loadLib(Tibrv.java:474)
    at com.tibco.tibrv.Tibrv.open(Tibrv.java:275)
[...]

Antes de nada, voy a contextualizar este error:
El entorno de trabajo es un Windows 7, con procesador Inter de 64 bits, la versión de Tibco Rendezvous es la 8.2 y tengo instalada una JDK de java versión 1.6.0_17.

Googleando el error no llego a ninguna solución en concreto, sólo conversaciónes acerca del classpath de la aplicación. Pero me he dado cuenta de que se deben dar unas condiciones:
– El fichero tibrvj.jar debe estar accesible en el classpath de la aplicación
– La variable de entorno PATH debe contener (entre otros) este valor:
%TIBRV_HOME%\bin
Siendo TIBRV_HOME el directorio de instalación de Tibco Rendezvous, por ejemplo y en mi caso es: "C:\tibco\tibrv\8.2"

Cumplo todas estas condiciones, pero sigo teniendo el error.

La excepción salta cuando en mi aplicación intento cargar la librería Tibrv mediante el código:

Tibrv.open(Tibrv.IMPL_NATIVE);

Como no puedo ver el código interno de la implementación de Tibco, intento cargar la
librería al más bajo nivel, mediante:

System.loadLibrary("tibrvj");

La librería debería cargar, ya que previamente he comprobado que el fichero tibrvj.dll está accesible a la aplicación, ya que ese fichero está localizado en el directorio %TIBRV_HOME%\bin, incluido en la variable de entorno PATH, y lo puedo comprobar dentro de mi aplicación java leyendo el valor de la variable de sistema java.library.path.

Al ejecutar el código System.loadLibrary("tibrvj"); obtengo la siguiente excepción:

[...]
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\TIBCO\tibrv\8.2\bin\tibrvj.dll: Can't load AMD 64-bit .dll on a IA 32-bit platform
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1758)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1683)
    at java.lang.Runtime.loadLibrary0(Runtime.java:823)
    at java.lang.System.loadLibrary(System.java:1028)
[...]

Bueno, ya tengo una pista. Resulta que estoy intentando cargar una librería desarrollada para 64bits en una plataforma (java, supongo) que es de 32bits. Luego, voy a comprobar mi versión instalada de java y si no es de 64bits tendré que instalar una de 64bits.

Compruebo mi versión de java:

C:\>java -version
java version "1.6.0_17"
Java(TM) SE Runtime Environment (build 1.6.0_17-b04)
Java HotSpot(TM) Client VM (build 14.3-b01, mixed mode, sharing)

(FAIL) No indica nada de que sea java de 64bits, por lo que supongo que será de 32.

Solución

Descargo la última versión de java 1.6 de 64bits, la instalo y vuelvo a comprobar la versión instalada:

C:\>java -version
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode)

Bien! ya tengo instalada una versión de java de 64bits.

Pruebo mi aplicación, …, FUNCIONA!

Conclusión

Todas las soluciones que encontré se centraban solamente en corregir las variables de entorno PATH y CLASSPATH, pero estas soluciones no eran suficientes para mí. Al menos, al acotar el problema mucho más y llegando al un nivel más bajo de abstracción obtuve una pista que me indicó que debería asegurarme de usar una versión de 64bits de java. Una vez instalada la versión correcta del JDK/JRE de java, todo funcionó.

Espero que esta información sea de utilidad a todos aquellos que tienen el problema de encontrarse con la excepción:

TibrvException[error=901,message=Library not found: tibrvj]

El código completo usado para demostrar este error se puede encontrar aquí:

import com.tibco.tibrv.Tibrv;
import com.tibco.tibrv.TibrvException;

public class TibrvLoaderTest {
	public static void main(String[] args) {
		new TibrvLoaderTest().start();
	}
	private void start() {
		printClasspath();
		printLibraryPath();
		testLoadLibrary();
		openTibrvjLibrary();
	}
	private void printClasspath() {
		String cp = System.getProperty("java.class.path");
		for(String cpElement : cp.split(";")){
			System.out.println("Classpath element: " + cpElement);
		}
	}
	private void printLibraryPath() {
		String lp = System.getProperty("java.library.path");
		for(String lpElement : lp.split(";")){
			System.out.println("Library path element: " + lpElement);
		}
	}
	private void testLoadLibrary() {
		System.loadLibrary("tibrvj");
	}
	private void openTibrvjLibrary() {
		try {
			Tibrv.open(Tibrv.IMPL_NATIVE);
		} catch (TibrvException e) {
			throw new RuntimeException("Can't load Tibrv", e);
		}
	}
}