Archive for November, 2008

Recursos en castellano

Carlos Ble ha tenido a bien incluirme en una lista de enlaces de recursos sobre TDD, refactoring y otras cosas ágiles. Muchas gracias, Carlos, sobre todo por compartir esa inquietud por las carencias de material en español.

Tags: , ,

El compromiso de un cerdo

La tira de hoy de Dilbert me ha hecho bastante gracia (aunque he de confesar que al principio no la entendí por el juego de palabras “dead pig” que luego comentaré).

Dilbert.com

Para los que tengáis problemas con el inglés, ahí va la traducción:

AS I GAZED AT MY BACON AND EGGS THIS MORNING, I REALIZED…

Mientras miraba esta mañana mis huevos con beicon me di cuenta de que…

…THE CHICKEN CONTRIBUTED, BUT THE PIG WAS COMMITED.

…el pollo había contribuido, pero el cerdo estaba comprometido.

(I AM SO CLEVER.)

(Soy tan listo.)

IF I PROMISE TO WORK LIKE A DEAD PIG, CAN I GO HOME EARLY?

Si prometo trabajar como un condenado(*), ¿me puedo marchar a casa antes?

(*) “dead duck” es una expresión que significa “condenado al fracaso”. Supongo que Scott Adams (o quien quiera que le escriba las tiras ahora) ha querido hacer un juego de palabras con “dead pig”, que literalmente significa “cerdo muerto” y que en castellano no tiene ningún sentido (y nada de gracia).

Esta tira me viene al pelo para comentar sobre los horarios en equipos ágiles. En mi última experiencia laboral trabajaba por objetivos y con horario flexible. Durante mucho tiempo me pregunté si es compatible la labor de programación con un horario flexible puesto que, yo al menos, tiendo a ocupar todo el tiempo posible en hacer la mejor solución posible. En este sentido, metodologías como Scrum, donde toda actividad está acotada en el tiempo (timeboxed) me vienen estupendamente porque me obliga a tener unos resultados en un tiempo previamente acotado, es decir, me obliga a tener un horario. ¿Quiere esto decir que mi compromiso es menor en comparación con otras maneras de organizar el trabajo? No, lo que quiere decir es que mi compromiso está acotado en el tiempo en aras de que éste sea sostenible (e.d. para evitar que nos quememos).

Frecuentemente he participado en conversaciones en las que se discutía sobre cómo se debe actuar (en el contexto de un proyecto que hace Scrum) cuando se termina una tarea mucho antes de lo que estaba previsto. En estos casos es el equipo el que decide (cuando digo equipo puedo estar hablando de un individuo, pero con el consentimiento más o menos tácito del resto) si le merece la pena comenzar con otra tarea o debe dejarlo para el día siguiente. Aquí hay varias “escuelas”:

  • los hay que prefieren dejar las cosas acabadas antes de irse a casa,
  • los hay que prefieren dejarlas comenzadas para no empezar desde cero por la mañana,
  • e incluso los que dejan el trabajo “casi listo” para cerrarlo al día siguiente con la mente más despejada.

Cada uno de los métodos tiene sus pros y contras, pero creo que es buena práctica el dejar que cada uno se responsable y se organice su propio trabajo.

Tags: , ,

¿Maven + Eclipse = m2eclipse?

Bueno, parece que últimamente me estoy especializando en traducciones del inglés al español porque estoy traduciendo (con permiso de los autores) un artículo sobre el plugin m2eclipse que apareció este verano en TheServerSide. Espero que nadie que sea experto en traducciones lea las mías porque podrá sacarme los colores fácilmente.

Por cierto, el artículo lo estoy traduciendo a ratitos y surgió como consecuencia de una pregunta que hice en la lista “ecosistemas-software” (que aprovecho para recomendar) en la que encuestaba sobre las alternativas para usar Maven en Eclipse: m2eclipse y Q4E.

Cuando tenga terminada toda la traducción os lo haré saber.

Editado:
He cambiado la dirección donde he publicado la traducción. Espero que si hay errores en la traducción me lo hagáis saber para corregirlos, gracias.

Tags: , ,

Refactoring en Español (y 6)

Hoy, por fin, termino esta serie de artículos basados en el ejemplo del primer capítulo del libro de Fowler “Refactoring“.

Recapitulemos un poco antes de comenzar.

Desde la primera entrega hasta ésta no hemos cambiado el comportamiento del sistema: sólo hemos cambiado el diseño interno. A eso le llamamos refactorizar. (Por cierto, mi siguiente entrega de “Aprender inglés es fácil… si sabes cómo” irá justamente sobre esta palabra).

Y estamos seguros de que no ha cambiado el comportamiento porque tenemos una batería de pruebas unitarias que así nos lo han ido demostrando.

A lo largo de las cinco entregas anteriores hemos ido moviendo código dentro de las clases hacia métodos privados que, además, mejoraban la comprensión del código. También hemos movido código entre clases, delegando responsabilidades entre objetos. Y finalmente, en la última entrega dejamos unas condiciones múltiples (switch) en los métodos getCharge y getFrequentRenterPoints en la clase Movie. Vamos a ver en este final del ejemplo cómo eliminar esos ifs, lo cuál me sirve de entradilla para comentar la campaña anti-if (originalmente en italiano, sí, en italiano).

Comenzamos.

At last… Inheritance

Para no plagiar, creo que lo mejor es recomendar directamente la lectura de este capítulo 1 del libro. Sin embargo, y aunque la explicación es excelente, hay que reconocer que el original está en inglés… :-)

Aunque no me dedico a la traducción y mi inglés es mejorable, voy a tratar de aportar mi granito de arena a la causa hispana.

AL FIN… HERENCIA

Tenemos varios tipos de películas que tienen diferentes maneras de responder a la misma pregunta. Esto parece un trabajo para subclases. [N.T. El autor se refiere a Movie.getCharge]. Podemos tener 3 subclases de Movie, cada una de las cuáles teniendo su propia versión de getCharge. (Ver Fig. 1.14)

[N.T. La fig. 1.14 muestra un diagrama de clases UML con las subclases RegularMovie, ChildrensMovie y NewReleaseMovie, heredando de Movie. Todas tienen un método llamado getCharge.]

Esto nos permite sustituir la instrucción switch por el uso de polimorfismo. Desgraciadamente esto tiene un pequeño problema: que no funciona. Una película puede cambiar su clasificación a lo largo de su vida. Un objeto no puede cambiar su clase a lo largo de su vida. Sin embargo, hay una solución en el patrón Estado [GoF]. Usando el patrón Estado, nuestro diseño quedaría como en la fig. 1.15

[N.T. La fig. 1.15 muestra otro diagrama de clases UML con la clase Price y sus subclases RegularPrice, ChildrensPrice y NewReleasePrice. Además, también muestra la clase Movie usando una instancia de Price para el cálculo de getCharge.]

Añadiendo la indirección podemos hacer las subclases del objeto Price y cambiar el precio cada vez que lo necesitemos.

Si estáis familiarizados con los patrones del GoF, quizás os preguntéis: “¿Es esto un estado o una estrategia?”. ¿Representa la clase Price a un algoritmo para calcular el precio (en cuyo caso yo preferiría llamarla Pricer o PrincingStrategy) o representa al estado de una película (Star Trek X es una novedad)? En este punto, la elección del patrón (y del nombre) refleja cómo queremos pensar acerca de la estructura. De momento estamos pensando en este objeto como el estado de una película. Si más adelante decidimos que una estragia comunica mejor nuestras intenciones, refactorizaremos cambiando los nombres.

Hasta aquí la traducción. Espero que os ayude a entender el cambio que vamos a introducir en el diseño y el por qué lo hacemos.

Vamos a introducir el patrón Estado. Para ello nos aseguramos de que tenemos bien encapsulados los accesos al atributo Movie._priceCode. (Lo cambiamos incluso en el constructor).

TESTS

Para introducir Price y toda la jerarquía hacemos TDD y, para ello, creamos PriceTest como sigue y lo añadiremos a la suite TestAll.

package step6;

import org.junit.Test;

public class PriceTest {

     public void testChildrensPrice() {
    Price aPrice = new ChildrensPrice();
    assertEquals(Movie.CHILDRENS,aPrice.getPriceCode());
  }

}

Lógicamente, como no compila, tendremos que crear las clases y métodos correspondientes. Al ejecutar los tests, salen en rojo (si hemos implementado getPriceCode con un valor por defecto), pero en cuanto hacemos que getPriceCode devuelva el valor esperado según la subclase que vayamos incorporando, volvemos al verde. Siempre primero el test y luego el código.

A continuación introducimos el uso de Price en Movie tal y como propone Fowler. De hecho, al copipegar su implementación de Movie.setPriceCode incorpora código para el que no teníamos pruebas. Esto lo descubrimos al ver el informe de cobertura de nuestras pruebas y comprobar que no tenemos una prueba para comprobar qué pasa cuando asignamos a una película un tipo de precio desconocido. (Sí, en sentido estricto no estaríamos refactorizando puesto que hemos cambiado los tests, e.d. la funcionalidad original, pero yo más bien diría que hemos encontrado un defecto en el código original puesto que no contemplaba una condición excepcional).

@Test(expected=IllegalArgumentException.class)
public void testMovieIllegalType() {
  new Movie(ANY_TITLE,UNKNOWN_PRICE_CODE);
}

Por lo demás, tras ejecutar nuestra batería de tests, el refactor ha ido bien. (Lo sabemos porque nuestros tests están en verde)

Siguiendo los pasos de Fowler, refactorizamos Movie.getCharge para que use un nuevo Price.getCharge (OJO, sin olvidar que estamos haciendo TDD) :-)

Implementamos Price.getCharge moviendo el código de Movie.getCharge a la clase abstracta (aunque este método no será abstracto).

No eliminamos el viejo test puesto que es lo que nos indicará que funciona o no nuestro refactor; pero implementamos los tests como el siguiente. (En realidad es fácil porque son idénticos a los de Movie)

 public void testChargeForChildrens() {
  Price aPrice = new ChildrensPrice();
  double charges[] = { 1.5, 1.5, 1.5, 3.0, 4.5, 6.0, 7.5 };
  for (int daysRented = 1; daysRented <= charges.length; daysRented++) {
    assertEquals(charges[daysRented-1], aPrice.getCharge(daysRented),0);
  }
}

TESTS

Declaramos el método ahora como abstracto e implementamos el primero que nos venga en gana (p.ej. ChildrensPrice.getCharge). Ya tenemos los tests, por lo que iremos refactorizando con seguridad. Recordad el lema de TDD: “RED GREEN REFACTOR“.

TESTS

A continuación, vamos haciendo los cambios correspondientes al resto de tipos de precio. Siempre con pasos seguros gracias a nuestra batería de tests.

TESTS

Y finalmente hacemos lo mismo con getFrequentRenterPoints:

  • movemos el código de Movie.getFrequentRenterPoints a Price.getFrequentRenterPoints (incluyendo primero los nuevos tests porque hacemos TDD)
  • no olvidamos mover los javadocs
  • implementamos el caso particular de NewRelease.getFrequentRenterPoints (dejando el caso general en la clase abstracta Price)

Pues bien, llegados a este punto ya hemos terminado con el ejemplo de Fowler. Os recomiendo encarecidamente el libro (en él se detallan las técnicas de refactor y los “tufillos” (smells) que nos indican cuándo es posible que nuestro código requiera ser refactorizado) y un par de secuelas muy interesantes:

  • Refactoring Workbook, con el que podréis ejercitar todo lo que hayáis aprendido con el de Fowler.
  • Refactoring to Patterns, con el que podréis orientar vuestros refactorajes hacia patrones de diseño, mejorando así la calidad de vuestros diseños.

Finalmente, una última recomendación: http://sourcemaking.com/refactoring

Desgraciadamente todas estas referencias están en inglés. :-(
¿Cuándo nos decidiremos a crear contenidos en español ahora que estamos reivindicando la profesión aquí en España?

Tags: ,

Eclipse 3.5 será Galileo

El nombre clave de la versión simultanea de la plataforma Eclipse 3.5 será Galileo. Aquí tenéis los planes separados por proyecto y el resumen a modo de tablero de mandos. Me encanta ver el enfoque agilista de la planificación separando los “Must Do” (DEBE HACER) de los “Should Do” (ESTARÍA BIEN QUE HICIERA).

Tags:

Huelga de Informáticos (II)

Acabo de dar por tristemente zanjado un debate sobre la regulación de nuestra profesión. Tuve un excelente debate con algunos, con otros tristemente no. Bueno, pero en este día y pico que he dedicado a este asunto, he sacado algunas conclusiones que me gustaría dejar por escrito.

Ayer me pasaron el enlace a un estupendo análisis sobre este asunto. El artículo del profesor Enrique Barreiro es excelente, salvo que (en mi opinión) falta tratar el tema de la regulación profesional. Hay un comentario en esa entrada de su blog donde alguien comenta justamente que sí a la regulación académica y no a la regulación profesional. (No creo que todo el mundo sea consciente de la diferencia). En el mismo blog publica un poco después una actualización sobre el tema que viene del Senado.

Mi opinión personal e intransferible:

  1. no creo que sea un problema como para una huelga, aunque sí para organizarse y plantear reivindicaciones de una vez
  2. no creo que formemos un colectivo organizado ni con suficiente formación ni organización como para plantear ningún conflicto social
  3. las asociaciones profesionales no están a la altura de las circunstancias puesto que no están aprovechando para poner orden en las discusiones (¿qué dicen la ALI, la ATI p.ej.? (El Colegio de Ingenieros Informáticos de Catalunya sí se ha posicionado claramente)
  4. nadie explica cómo se está abordando el mismo problema (las competencias académicas y la homologación de titulaciones) en el resto del “territorio Bolonia” (De hecho, a nadie parece importarle cómo se hacen las cosas fuera de nuestras fronteras)
  5. no creo que sea IMPRESCINDIBLE una regulación exhaustiva de las titulaciones porque eso impediría flexibilidad para adaptarse a la realidad cambiante (los avances tecnológicos)
  6. NO a la regulación profesional (por las razones que ya daba en el artículo anterior)

En cualquier caso, y aunque no la secundaré porque no quiero respaldar una posible regulación profesional en términos de exclusión, espero que la movilización tenga todo el éxito posible en cuanto a conseguir el mismo tratamiento que el resto de ingenierías (resulta cuanto menos chocante que no aparezca la Ingeniería Informática en el listado de asociados a la federación de asociaciones de ingenieros del Instituto de la Ingeniería de España).

Suerte, compañeros y compañeras.

Tags:

Huelga de Informáticos

Supongo que ya lo sabréis, pero en España hay convocada una huelga en defensa de los Ingenieros Informáticos. Según el folleto informativo de los convocantes (la RITSI):

Las reivindicaciones de los estudiantes son:
1) Obtener del Ministerio de Ciencia e Innovación el compromiso de elaborar una ficha de directrices específicas para las titulaciones de Ingeniería Informática, en las mismas
condiciones que el resto de ingenierías.
2) Obtener del gobierno el compromiso de incluir en la transposición de la directiva europea de servicios las profesiones de Ingeniero Técnico y de Ingeniero en
Informática.
3) Obtener del Ministerio de Industria el compromiso de elaborar y llevar al Parlamento una ley que regule las atribuciones profesionales del sector.

He estado cruzando algunas preguntas y opiniones en un foro que se ha creado para la ocasión y he llegado a la conclusión de que puedo estar de acuerdo con las dos primeras reivindicaciones, pero no con la tercera. Permitid que me “autocite”:

Para mi, mi título es un mero adorno. Lo que no es un adorno es la
formación que recibí para obtenerlo. Por eso lo que pido es mejor
formación para los titulados, porque eso pone en valor mi propia
formación. Yo lo que pido es que cuando diga en mi CV que soy
Licenciado (o incluso Ingeniero) en Informática no tenga que explicar
nada más y todo el mundo asuma el valor intrínseco de ese título. Pero
no quiero que un compañero mío que se haya formado de una manera no
reglada (p.ej. mediante cursos especializados) y que aporte un gran
valor a la profesión tenga que pedirme permiso a mi para poner en
marcha un proyecto sólo porque yo tengo un papel y él no. Quizás porque
sea Licenciado (y no Ingeniero), o quizás por la edad, tengo una visión
un tanto romántica de nuestra profesión. :oops:

Tags:

Refactoring en Español (5)

Por fin he conseguido sacar tiempo para continuar con la serie basada en el ejemplo del primer capítulo del libro “Refactoring” de Martin Fowler.

En esta entrega vamos a ver cómo nos llevamos la lógica condicional que hay en los métodos Rental.getCharge y Rental.getFrequentRenterPoints a la clase Movie.

Replacing the Conditional Logic on Price Code with Polymorphism

Fowler afirma que es una mala idea basar un switch en un atributo que corresponde a otro objeto. Dado que se basa en un atributo de Movie, el método debería estar en Movie (aunque tengamos que pasar el número de días de alquiler como parámetro).

Los pasos a seguir son los siguientes (suponiendo que tenemos Eclipse):
1. Alt+Shift+V sobre el método Rental.getCharge

  • Seleccionamos Movie
  • Marcamos la opción de crear un método delegado (pero sin marcarlo como @deprecated)
  • Revisamos el javadoc porque ahora el método está en otro objeto y puede tener otras responsabilidades.

Con esto, habremos dejado el código como sigue:

 /**
  *  Movie#getCharge(int)
  */
 double getCharge() {
  return _movie.getCharge(getDaysRented());
 }
 /**
  * Determine the corresponding amount depending on the type of movie
  * (the price code).
  *
  *  rental TODO
  *    */
 double getCharge(Rental rental) {
  double thisAmount = 0;
  switch (rental.getMovie().getPriceCode()) {
    case Movie.REGULAR:
      thisAmount += 2;
      if (rental.getDaysRented() > 2)
        thisAmount += (rental.getDaysRented() - 2) * 1.5;
      break;
    case Movie.NEW_RELEASE:
      thisAmount += rental.getDaysRented() * 3;
      break;
    case Movie.CHILDRENS:
      thisAmount += 1.5;
      if (rental.getDaysRented() > 3)
        thisAmount += (rental.getDaysRented() - 3) * 1.5;
      break;
   }
   return thisAmount;
 }

TESTS

2. El atributo sobre el que se hace el switch es equivalente a getPriceCode() puesto que se trata del mismo objeto Movie; así que esa linea pasa de:

  switch (rental.getMovie().getPriceCode()) {

a:

  switch (getPriceCode()) {

TESTS

3. Cambiamos el atributo que pasamos para que, en vez de pasar el objeto Rental, pasar el número de días que se alquila.

  • Hay que cambiar todas las rental.getDaysRented por el parámetro daysRented
  • Cambiamos el javadoc porque ahora el método recibe parámetros distintos.

El código de este método queda como sigue:

 /**
  * Determine the corresponding amount depending on the type of movie
  * (the price code) and the number or days rented.
  *
  *  daysRented
  *    */
 double getCharge(int daysRented) {
  double thisAmount = 0;
  switch (getPriceCode()) {
    case Movie.REGULAR:
      thisAmount += 2;
      if (daysRented > 2)
        thisAmount += (daysRented - 2) * 1.5;
      break;
    case Movie.NEW_RELEASE:
      thisAmount += daysRented * 3;
      break;
    case Movie.CHILDRENS:
      thisAmount += 1.5;
      if (daysRented > 3)
        thisAmount += (daysRented - 3) * 1.5;
      break;
    }
    return thisAmount;
 }

TESTS

Por simetría (un importante patrón, ver “Implementation Patterns” de Beck), implementamos el método Rental.getFrequentRenterPoints en Movie (pasando el número de días de alquiler como parámetro).

Lo dejo como ejercicio porque es simplemente aplicar la misma receta sobre el método Rental.getFrequentRenterPoints llevando el código al nuevo Rental.getFrequentRenterPoints(days:int).

TESTS

Llegados a este punto… me doy cuenta de que no estamos usando TDD. Es decir, que los tests que tengo no están probando el código que estoy escribiendo. Me refiero a que hemos movido código entre clases y no hemos escrito tests para estas clases.

He escrito MovieTest.

package step5;

import static org.junit.Assert.assertEquals;
import org.junit.Ignore;
import org.junit.Test;

public class MovieTest {
 private static final String ANY_TITLE = "Any Movie Title";
 private static final int UNKNOWN_PRICE_CODE = 99;
   public void testMovieRegular() {
  Movie aMovie = new Movie(ANY_TITLE,Movie.REGULAR);
  assertEquals(Movie.REGULAR, aMovie.getPriceCode());
 }

   public void testMovieChildren() {
  Movie aMovie = new Movie(ANY_TITLE,Movie.CHILDRENS);
  assertEquals(Movie.CHILDRENS, aMovie.getPriceCode());
 }

   public void testMovieNewRelease() {
  Movie aMovie = new Movie(ANY_TITLE,Movie.NEW_RELEASE);
  assertEquals(Movie.NEW_RELEASE, aMovie.getPriceCode());
 }

 @Test(expected=IllegalArgumentException.class)
   public void testMovieIllegalType() {
  new Movie(ANY_TITLE,UNKNOWN_PRICE_CODE);
 }

   public void testFrequentRenterPointsRegular() {
  int points[] = { 1, 1, 1 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.REGULAR);
  for (int daysRented = 1; daysRented <= points.length; daysRented++) {
   assertEquals(points[daysRented-1], aMovie.getFrequentRenterPoints(daysRented));
  }
 }

   public void testFrequentRenterPointsChildrens() {
  int points[] = { 1, 1, 1 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.CHILDRENS);
  for (int daysRented = 1; daysRented <= points.length; daysRented++) {
   assertEquals(points[daysRented-1], aMovie.getFrequentRenterPoints(daysRented));
  }
 }

   public void testFrequentRenterPointsNewRelease() {
  int points[] = { 1, 2, 2, 2 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.NEW_RELEASE);
  for (int daysRented = 1; daysRented <= points.length; daysRented++) {
   assertEquals(points[daysRented-1], aMovie.getFrequentRenterPoints(daysRented));
  }
 }

   public void testChargeForRegular() {
  double charges[] = { 2.0, 2.0, 3.5, 5.0, 6.5, 8.0 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.REGULAR);
  for (int daysRented = 1; daysRented <= charges.length; daysRented++) {
   assertEquals(charges[daysRented-1], aMovie.getCharge(daysRented),0);
  }
 }

   public void testChargeForChildrens() {
  double charges[] = { 1.5, 1.5, 1.5, 3.0, 4.5, 6.0, 7.5 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.CHILDRENS);
  for (int daysRented = 1; daysRented <= charges.length; daysRented++) {
   assertEquals(charges[daysRented-1], aMovie.getCharge(daysRented),0);
  }
 }

   public void testChargeNewRelease() {
  double charges[] = { 3.0, 6.0, 9.0, 12.0 };
  Movie aMovie = new Movie(ANY_TITLE,Movie.NEW_RELEASE);
  for (int daysRented = 1; daysRented <= charges.length; daysRented++) {
   assertEquals(charges[daysRented-1], aMovie.getCharge(daysRented),0);
  }
 }
}

Añadimos este test a la suite y volvemos a lanzarlos todos.

En resumen, el cambio que hemos hecho ha consistido en crear los métodos Movie.getCharge(days:int) y Movie.getFrequentRenterPoints(days:int) para alojar ahí la lógica de los métodos homónimos en la clase Rental y que dependían de una propiedad de la clase Movie.

En la siguiente entrega (la última), donde haremos un cambio muy serio al diseño puesto que introduciremos nuevas clases (entre ellas Price), podremos ver cómo recuperaremos la inversión que acabamos de hacer en los nuevos tests.

Tags:

XML en el BOE

La verdad, debo estar algo chapado a la antigua, pero me ha sorprendido el encontrarme un trozo de XML en un BOE, concretamente el XSD de la factura electrónica. Podéis encontrar más información en http://www.facturae.es

Por cierto, ¿alguien conoce a alguien que esté trabajando en estos temas? ¿y con UBL?

Tags: