domingo, 30 de enero de 2011

LibreOffice: el nuevo OpenOffice

LibreOffice se puede considerar la continuación de OpenOffice, y su primer versión estable ya la pueden encontrar en el siguiente link. Cuando Oracle adquirió Sun (el principal promotor de OpenOffice), la comunidad temió que se detuviera el desarrollo de este excelente conjunto de herramientas, por ello crearon "The Document Foundation" para continuar con el desarrollo sin tener dependencias. Puedes encontrar más detalles del nacimiento de LibreOffice en el siguiente wiki.

¿Qué opinan del nacimiento de LibreOffice? ¿Ya lo instalaron?

domingo, 23 de enero de 2011

Mercurial y Bitbucket (parte 2)

Te encuentras en el segundo post acerca de Mercurial y Bitbucket:
  1. Mercurial y Bitbucket (parte 1)
  2. Mercurial y Bitbucket (parte 2)

En el post anterior mencioné los beneficios de utilizar Mercurial en el desarrollo de un proyecto ya sea de forma individual o en un equipo de desarrollo. También hablé de Bitbucket, un sitio donde se pueden almacenar repositorios. En este post mostraré cómo se utilizan ambas herramientas en un nivel muy básico.

Lo primero que se necesita es bajar la aplicación TortoiseHg, que contiene el software necesario para instalar Mercurial. Una vez seleccionado el archivo adecuado de Tortoise dependiendo del sistema operativo que estés utilizando, ejecútalo y sigue las instrucciones del wizard de instalación. Esto es muy sencillo, únicamente hay que observar las opciones que nos provee dicho wizard y hacer click en Next (Siguiente).

Una vez que se ha instalado correctamente Mercurial, hay que adquirir el repositorio para almacenar nuestro proyecto. Para ello, dirígete a la página de Bitbucket y crea una cuenta. Esto se logra primero, dando click en el botón Sign Up y llenando los campos que son necesarios para poder adquirir la cuenta. El uso de Bitbucket es gratuito para un equipo de hasta 5 personas, si estás trabajando en un grupo más grande, tal vez debas echarle un vistazo a las tarifas con las que cuenta.

La forma de registrarte en Bitbucket es dando click en el botón de Sign Up:

Recibirás un correo de bienvenida por parte del equipo de desarrollo de Bitbucket. Ingresa a tu cuenta de Bitbucket y crea un repositorio nuevo:

Dependiendo de las características de tu proyecto, tu nuevo repositorio puede ser público o privado. Si es privado, no podrá ser accedido por alguien más a menos que le otorgues los permisos necesarios (la persona que vaya a trabajar con este repositorio independientemente de si es privado o no, debe contar con su propia cuenta en Bitbucket).

Todos los colaboradores deben tener su copia local del repositorio, para obtener una copia local, debes abrir la consola y utilizar el comando hg clone seguido de la url del repositorio a copiar. Nota: todos los comandos de Mercurial de los que hablaré aquí comienzan con "hg" (que curiosamente es el símbolo en la tabla periódica para el Mercurio).

Después de crear y clonar el nuevo repositorio, ya puedes empezar a trabajar con él. Veamos cómo trabajar con un repositorio a través de un ejemplo donde utilizo el proyecto de los árboles binarios (llamado Trees) de la serie Desarrollo de Software dirigido por Pruebas.

Paso 1: Verificar estado de la copia local.

Antes de iniciar un nuevo cambio, ejecuto el comando hg status para asegurarme de que no tengo cambios pendientes.

Paso 2: Obtener los cambios del repositorio en línea.

Ejecuto el comando hg incoming, que me dirá cuáles han sido los cambios que se han regresado al repositorio en línea. De todos los cambios que observo, selecciono aquéllos que considero necesarios para la parte del proyecto que me tocó desarrollar.Para observar cuáles han sido todos los cambios, utilizo hg incoming. Para obtener los cambios que requiero, uso hg pull, y finalmente, para actualizar mi área de trabajo local, utilizo hg update.

Paso 3: Realizar los cambios necesarios.

En este paso, realicé las modificaciones necesarias. Si agregas un nuevo archivo a tu área de trabajo local, utiliza el comando hg add. Al finalizar mis cambios utilicé nuevamente el comando hg status para corroborar los archivos que haya modificado.

Paso 4: Confirmar cambios localmente.

Debido a que cambió el estado de mi repositorio local, debo confirmar mis movimientos, y para ello, utilizo el comando hg commit -m "Mensaje o nota aclaratoria". Este comando se usa cada vez que terminas un cambio dentro del proyecto que puede consistir en una sola acción o un conjunto de acciones. Con él, se le dice a Mercurial que se autorizan los cambios localmente. Los mensajes o notas aclaratorias que siguen al comando hg commit son de gran utilidad, ya que sirven para saber en qué cambió el proyecto de una manera clara y concisa.

Paso 5: Regresar cambios al repositorio en línea.

Una vez que estamos seguros de que los cambios fueron los adecuados, los regresamos al directorio en Bitbucket. Para saber qué cambios se van a regresar, utilizo hg outgoing y finalmente, para enviarlos, uso hg push. Todos los cambios realizados localmente se devuelven al repositorio en línea y todos los colaboradores pueden observar cuáles fueron esos cambios y quién los realizó.

La siguiente imagen muestra gráficamente los cinco pasos anteriores:

Bitbucket te permite observar todos los cambios realizados a tu proyecto de manera gráfica. Además, con Tortoise también lo puedes hacer de forma local. Solamente tienes que dar click derecho al folder del proyecto y seleccionar Hg Repository Explorer. Con él obtendrás algo parecido a lo que se observa en la siguiente imagen:

Mercurial es una herramienta que puede facilitar enormemente la administración de un proyecto, además de que su uso es muy sencillo. Es importante saber que existen este tipo de aplicaciones, ya que en el desarrollo de software, el control sobre el desarrollo de un proyecto, es un aspecto clave que determina muchas veces el grado de éxito o fracaso con el que los desarrolladores pueden enfrentarse. Cabe aclarar, que esta es solamente una introducción a lo que puedes realizar con la ayuda de Mercurial, pero espero que después de haberla leído estés listo para descubrir el mundo de Mercurial.

jueves, 20 de enero de 2011

Mercurial y Bitbucket (parte 1)

Te encuentras en el primer post acerca de Mercurial y Bitbucket:
  1. Mercurial y Bitbucket (parte 1)
  2. Mercurial y Bitbucket (parte 2)

El trabajo en equipo es y será el pan nuestro de cada día. Algunas actividades como la realización de presentaciones o de material didáctico para alguna exposición quizá son relativamente fáciles de realizar en forma grupal. Sin embargo, actividades como la programación, por ejemplo, son mucho más difíciles de sobrellevar en equipo ya que en el caso de ésta, es claro que cada persona piensa y codifica de una manera en particular y llegar a un consenso es quizá, el mayor de los triunfos posibles.

En el mejor de los casos, usualmente creamos varias copias del proyecto conforme vamos dándole forma y una persona se encarga luego de juntar los cambios y obtener una nueva versión de éste. Pero con esta forma de proceder poco podemos saber al final sobre quién realizó qué cambios y si existen errores, quién es el responsable y por lo tanto el indicado para arreglarlos sin mencionar que si la persona encargada de unificar todos los cambios comete un error, todo el esfuerzo de el equipo puede convertirse en un rotundo fracaso.

¿No sería ideal que existiera una aplicación que permitiera administrar fácilmente los cambios realizados a un proyecto, que llevara el historial de dichos cambios, que permitiera que todos los miembros del equipo trabajaran desde la comodidad de sus equipos y fuera fácil de utilizar?

Pues bien, este tipo de herramienta existe y se le conoce con el nombre de Control Version System. Una de las aplicaciones de este tipo más conocidas es Mercurial (que además es gratuito).

Pero, ¿porqué utilizar Mercurial? Observa las siguientes imágenes. Así es como quizá estamos acostumbrados a manejar nuestros proyectos:

Y así es como podríamos estar manejando dichos proyectos gracias a Mercurial:

El repositorio único que se observa en la segunda imagen es el lugar donde se almacena el proyecto. Mercurial es la herramienta para interactuar con dicho proyecto y en este caso estoy utilizando el servicio ofrecido por Bitbucket para alojar repositorios Mercurial.

Mercurial no solamente es útil cuando se trabaja en equipo, aún si se trata de un proyecto personal puede resultar de gran ayuda en la administración del mismo. Precisamente uno de los proyectos en los que he estado trabajando recientemente, llamado Trees (que ilustra la serie de posts titulada Desarrollo de Software dirigido por Pruebas), lo he estado administrando con Mercurial y Bitbucket.

¿Pero exactamente cómo se usan Mercurial y Bitbucket?

En el próximo post explicaré lo que se necesita hacer para comenzar a utilizar esta herramienta, los pasos a seguir para crear un repositorio y algunos comandos básicos de Mercurial.

domingo, 16 de enero de 2011

Desarrollo de Software dirigido por Pruebas: árboles binarios (Parte 3)

Te encuentras en la tercera parte de la serie "Desarrollo de Software dirigido por Pruebas":
  1. Diseño de las pruebas
  2. Implementación
  3. Refactoring
  4. Conclusiones y código

Refactoring

En ocasiones cuando un proyecto va creciendo y va adquiriendo más funcionalidad y responsabilidades, sin darnos cuenta podríamos estar repitiendo líneas de código en distintas partes del proyecto y por ende, dar mantenimiento a un proyecto donde se repita el código en distintas partes puede resultar una tarea engorrosa, difícil y propensa a errores, ya que cada vez que exista un cambio, se realiza normalmente de forma manual, y si olvidamos actualizar el código en algún lugar donde sea necesario, seguramente habrá problemas a la hora de correr nuestras pruebas. Por ejemplo, en la clase SimpleBinaryTree existía un error en la implementación del método de borrado, misma que detecté en una de las pruebas cuando copié el código para reutilizarlo en la clase AVLTree. Cuando arreglé el error, únicamente lo hice dentro da la clase AVLTree, de modo que el código del borrado en la clase SimpleBinaryTree, seguía siendo incorrecto.

Las líneas repetidas podrían concentrarse en un solo lugar y de ahí ser llamadas por aquéllos miembros del proyecto que están ocupándolas, haciéndolo más accesible para su mantenimiento sin afectar su correcto funcionamiento. El proceso de identificar código repetido que puede ser reusado y colocarlo en un solo lugar para hacerlo disponible a los métodos o clases que lo estén utilizando es parte de las técnicas de Refactoring o Code Refactoring.

Refactoring de hecho es parte del proceso de Desarrollo de Software dirigido por Pruebas. Esta es la razón por la cual decidí hablar de él en esta tercera parte de la serie. Las clases SimpleBinaryTree y AVLTree del proyecto Trees con el cual he venido ejemplificando este proceso, se encuentran en un punto en el cual, a pesar de funcionar correctamente, (es decir, todas las pruebas que diseñamos previamente a su implementación corren exitosamente) ambas clases contienen exactamente el mismo código para insertar y borrar un nodo dentro de un árbol. La única diferencia es la implementación de rotaciones en la clase AVLTree que permiten el balanceo del árbol.

Por lo tanto, existe código repetido en ambas clases y el siguiente paso es, precisamente la realización del refactoring que permitirá reducir visiblemente el número de líneas de código en dichas clases sin afectar su funcionamiento. La manera de demostrar esto último será al volver a correr las pruebas con las que ya contábamos y que corrían exitosamente antes del proceso de refactoring: si las pruebas vuelven a correr exitosamente, habremos realizado el refactoring de manera apropiada.

De manera similar, los procesos de rotación que se llevan a cabo en la clase AVLTree, se reutilizarán en la implementación de la clase RedBlackTree, por lo tanto, podemos realizar también el refactoring para los métodos de rotación que se encuentran actualmente en la clase AVLTree.

En la siguiente imagen se aprecian los métodos de la clase AVLTree antes de realizar el refactoring y los resultados de las pruebas desarrolladas tanto para esta clase como para SimpleBinaryTree. Como pueden observar, ambas clases funcionan correctamente.

Ahora bien, podríamos simplemente mover el código referente a la inserción, al borrado y a las rotaciones a la clase abstracta BinaryTree, la cual es heredada por todas las demás, sin embargo, esto no es lo más correcto, ya que las rotaciones no son necesarias en la clase SimpleBinaryTree. Para evitar este problema, creé la clase abstracta BalancedBinaryTree, la cual también hereda a BinaryTree pero se encarga únicamente de las rotaciones. De este modo, BalancedBinaryTree adquiere toda la funcionalidad de BinaryTree, pero únicamente las clases que la hereden podrán adquirir la funcionalidad de las rotaciones y en este caso, las clases que la heredarán serán AVLTree y RedBlackTree. La inserción básica y el borrado básico pueden permanecer en BinaryTree ya que todas las clases que hereden a BinaryTree deben contar con métodos de inserción y borrado de nodos.

La nueva relación existente entre las clases queda ilustrada a través de este diagrama:

Después de haber realizado todos estos cambios, volvemos a ejecutar las pruebas y observamos los resultados. En la imagen de abajo se puede observar cómo se redujo de manera dramática el cuerpo de la clase AVLTree después de haber realizado el refactoring, pero lo más importante es que las pruebas tanto para AVLTree como para SimpleBinaryTree siguen siendo exitosas. Esto quiere decir que a pesar de todos los cambios que realizamos, el proyecto sigue funcionando correctamente y esta es una de las grandes ventajas de las pruebas: podemos asegurarnos de que nuestro programa sigue funcionando después de haber realizado varios cambios con tan sólo volver a correr las pruebas que diseñamos al principio y verificar que éstas siguen siendo exitosas. Si ocurre algún error, los mensajes que se despliegan en la ventana de Test Results son de gran ayuda para localizar las líneas de código donde se está presentando el problema.

Todo lo que hasta ahora hemos realizado forma parte del ciclo de Desarrollo de Software Dirigido por Pruebas: hemos creado las pruebas, las corrimos para observar que éstas fallan pues en ese momento no existía la implementación de los métodos que estamos probando, después realizamos la implementación de dichos métodos, acto seguido volvimos a correr las pruebas y cuando éstas fueron todas exitosas, concentramos nuestra atención en el proceso de refactoring a modo de mejorar la estructura del proyecto y de hacerlo más fácil de mantener, el último paso consiste en la repetición de cada una de estas etapas cada vez que se quiere agregar funcionalidad al proyecto.

En los próximos posts, hablaré de la implementación de la clase RedBlackTree, que es la última clase del proyecto que hemos estado analizando, por lo que estaré llevando a cabo el último paso del ciclo mencionado: la repetición de todos los pasos comenzando siempre por la elaboración de las pruebas.

sábado, 15 de enero de 2011

Desarrollo de Software dirigido por Pruebas: árboles binarios (Parte 2)

Te encuentras en la segunda parte de la serie "Desarrollo de Software dirigido por Pruebas":
  1. Diseño de las pruebas
  2. Implementación
  3. Refactoring
  4. Conclusiones y código
En el post anterior, definí el procedimiento de Desarrollo dirigido por Pruebas, también expuse su importancia y comencé a aplicar esta técnica en la implementación de tres tipos de árbol binario: árbol binario simlple, árbol AVL y Red-Black. En este post, mostraré con mayor detalle la organización de este proyecto así como el resultado de correr las pruebas elaboradas anteriormente.

Utilizando Netbeans implementé una clase abstracta llamada BinaryTree que servirá de base para crear las clases SimpleBinaryTree, AVLTree y RedBlackTree. Adicionalmente, implementé una clase llamada TreeNode, que se encarga de crear objetos tipo nodo, los cuales forman parte de la estructura de los objetos tipo árbol binario.

El siguiente diagrama de UML muestra la estructura del proyecto y las relaciones existentes entre las clases.


En este diagrama se muestran los métodos más importantes para cada clase. Nuevamente me gustaría reafirmar que hasta este punto, los cuerpos de dichos métodos están vacíos. En Netbeans, la organización del proyecto es la siguiente:


¿Qué sucede si corremos las pruebas en este punto? Dado que aún no existe la implementación de los métodos a probar, al correr las pruebas obtendremos mensajes de error, tal y como se muestra a continuación.



En este momento ya sabemos qué es lo que la implementación de los métodos debe arrojar y también, a través de la elaboración de las pruebas, pudimos visualizar más claramente la manera en que realizaremos dicha implementación. Pues bien, ahora sí podemos comenzar a codificar los cuerpos de los métodos.

Probablemente, al terminar de codificar algunas pruebas fallen, pero los mensajes que aparecen en la ventana de Test Results pueden ayudarnos a observar en dónde se está presentando la conduca anómala y su corrección puede ser más rápida.

Además, si cada prueba está enfocada a evaluar un aspecto en particular de la implementación, nos pueden ayudar a aislar errores. Por ejemplo, en el caso del borrado de nodos diseñé pruebas para los tres casos: el borrado de una hoja, el borrado de un nodo con un hijo y el borrado de un nodo con dos hijos. Al correr las pruebas después de la implementación del borrado, se presentó un error en la prueba del borrado de un nodo con dos hijos. Esto me ayudó a enfocarme en corregir la parte del código que se encarga de este caso. Cuando todas las pruebas pasen, podemos tener mayor seguridad de que la implementación es correcta:


La clase que implementé hasta ahora es SimpleBinaryTree. En los siguientes posts, realizaré el mismo procedimiento (pruebas primero, implementación después) para las clases AVLTree y RedBlackTree.

martes, 4 de enero de 2011

Respuesta a la Trivia de SQL Básico

En octubre lancé una trivia para cerrar la serie de SQL básico. En este post vamos a revisar el proceso que usé para solucionar la trivia. Algunos de los pasos y consultas los realicé para poder, de cierta forma, corroborar si los resultados tenían sentido. La pregunta es la siguiente:

"Mostrar la consulta y el resultado que obtenga el par de actores que han trabajado en más films juntos. Por ejemplo, quizá Cristopher y Bela Walken han trabajado en 50 films. La consulta debe regresar a los dos actores y el número de films. NOTA: Cristopher no es necesariamente parte de la respuesta, puede ser cualquier par de actores."

Primero, obtengamos el número de registros en la tabla film_actor y film.

select count(*) from film_actor;
select count(*) from film;

Hay 1000 films y 5462 registros en film_actor. Es decir, hay en promedio 6 (5462/1000) actores por film. Si formamos parejas con los 6 actores de un film, tenemos un total de 30 parejas por film - cada actor hace pareja con los otros 5 actores. Noten, que para un mismo film una pareja aparece 2 veces: la pareja Actor A con Actor B y la pareja Actor B con Actor A. Esto último no afecta el resultado final, pero es importante tenerlo en cuenta.

Dado que hay 1000 films y aproximadamente 30 parejas por film, la primer consulta que vamos a construir debe regresar alrededor de 30,000 registros correspondientes a todas las parejas de todos los films. La siguiente consulta cuenta todas las parejas:

select count(*)
from actor
    inner join film_actor on (actor.actor_id = film_actor.actor_id)
    inner join film_actor colega on 
               (film_actor.film_id=colega.film_id and 
                actor.actor_id <> colega.actor_id)
    inner join actor actor_colega on 
               (colega.actor_id=actor_colega.actor_id)

Esta consulta regresa 29,830; lo cual es muy cercano al estimado que calculamos arriba. Ahora, analicemos los joins de la consulta. El primer join relaciona un actor con los films en los que ha participado. El segundo y tercer join relacionan a un actor con todos sus colegas a lo largo de todas los films en los que ha participado. Veamos un ejemplo, la siguiente consulta regresa los actores que participaron en el film 500

select actor.first_name, actor.last_name
from actor
    inner join film_actor on (actor.actor_id = film_actor.actor_id)
where film_actor.film_id=500

Ahora agreguemos los dos joins para calcular las parejas de ese film. Noten que este film tiene 7 actores, por lo que habrá 42 parejas.

select actor.first_name, actor.last_name, 
       actor_colega.first_name, actor_colega.last_name
from actor
    inner join film_actor on (actor.actor_id = film_actor.actor_id)
    inner join film_actor colega on 
               (film_actor.film_id=colega.film_id and 
                actor.actor_id <> colega.actor_id)
    inner join actor actor_colega on 
               (colega.actor_id=actor_colega.actor_id)
where film_actor.film_id=500

La siguiente imagen muestra todas las parejas para el film 500. Observen que todas las parejas aparece dos veces, por ejemplo: la pareja CUBA OLIVIER y HELEN VOIGHT y la pareja HELEN VOIGHT y CUBA OLIVIER. Sin embargo, como mencioné arriba, esto no afectará el resultado final.

Muy bien, ya tenemos las parejas, sólo tenemos que agrupar y contar. La siguiente consulta despliega la pareja que más films ha hecho.

select actor.first_name, actor.last_name, 
       actor_colega.first_name, actor_colega.last_name, 
       count(*) as n
from actor
    inner join film_actor on (actor.actor_id = film_actor.actor_id)
    inner join film_actor colega on 
               (film_actor.film_id=colega.film_id and 
                actor.actor_id <> colega.actor_id)
    inner join actor actor_colega on 
               (colega.actor_id=actor_colega.actor_id)
group by actor.first_name, actor.last_name, 
         actor_colega.first_name, actor_colega.last_name
order by n DESC
LIMIT 1

Y los ganadores son HENRY BERRY y JULIA MCQUEEN con 7 films juntos. Se puede dar el caso de que haya más de una pareja con 7 films. Si en la consulta anterior, cambias a LIMIT 10, podrás ver las primeras 10 filas y notarás que después de la pareja ganadora, siguen otras parejas con sólo 6 films.