Simetría

Lo siento, si alguien espera un post navideño (a pesar de los copitos cayendo por el blog) al estilo retrospectiva o propósitos de nuevo año, que se vaya buscando otro artículo. Creo que soy de esos que no tiene mucho apego por la Navidad. Sin embargo, ¡Feliz paso de año y que el 2011 os trate mejor que el 2010! (A todos, aunque hayamos sido -algo- malos) 🙂

Bueno, al tema.

Picado por no haber podido estar ayer en el codingdojo de (la comunidad de Ruby en Madrid a la que, por fin, se acercó mucha gente que conozco), estaba yo practicando la codekata de los números romanos (una parte de ella, realmente, la de pasar a “romano”) y se me ocurrió hacer un pequeño cambio. En vez de usar un java.util.Map (sí, en Java, qué pasa 🙂 ) pensé en usar un array. Pensé que no debería afectarme mucho porque había escrito mi código haciendo mi mejor TDD (lo cuál no quiere decir que lo haga bien).

Para que tengáis una primera imagen de lo que hablo, mi método “String to_roman(int n)” es como sigue:

NUMERALS es un Map<Integer,String> ordenado para que “int find_biggest_numeral_that_fits(int i)” sea más fácil (y de paso eficiente). Pero lo que os quiero contar no es si es mejor usar un Map o un array para guardar esta tabla. De hecho, sobre esto se me ocurre otro artículo muy chulo sobre SRP (aunque no pueda superar éste de Hadi Hariri, claro). El caso es que, al cambiar la estructura de datos que soporta esto, mi algoritmo ha tenido que cambiar incluso hasta el método de más alto nivel de abstracción “to_roman”.

Recordemos el “Implementation Patterns” que, aunque no lo tengo a mano, recuerdo que hace hincapié en cuidar que nuestros métodos mantengan un nivel de abstracción coherente. En este caso es evidente que estamos llamando a “find_biggest_numeral_that_fits” para abstraernos de los detalles técnicos pero luego, en el mismo “to_roman” usamos NUMERALS para obtener el valor correspondiente. Hemos introducido una asimetría que no es muy dañina (en este caso que el problema es pequeño, que por eso es pequeño, para que podamos practicar sin que nos duela tanto como “en la vida real”).

Al refactorizar he cambiado “find_biggest_numeral_that_fits” por una llamada a “find_biggest_numeral_using_an_ordered_map” y ahí, al cambiar mi solución basada en un array y llamar al nuevo “find_biggest_numeral_using_an_ordered_array” me he dado cuenta de que no he cambiado NUMERALS y que sigue siendo un Map. .

He renombrado NUMERALS a NUMERALS_AS_MAP para que sea más evidente y así, he ocultado ese “código feo” en un bonito “String get_numeral(int i)”, lo cuál deja el método “to_roman” mucho más limpio.

Por cierto, observad también el leve cambio que he hecho en el orden de la linea 6, donde inicializo el StringBuffer. Me ha parecido que inicializarla “tan lejos” de donde la uso le daba un cierto tufillo que no me gustaba nada. ¿A que se lee mejor ahora? 🙂

Espero vuestros comentarios. Seguro que se puede mejorar mucho aún.

Ea, lo dicho, ¡Feliz 2011!

  • Tres cosillas.

    El nombre del método, a menos que sea una extensión del tipo int, debería haber sido intToRoman, o mejor aún intToRomanNotation.

    ¿n? Me duelen los ojos de ver eso. numero, es lo mínimo.

    No controlas si int viene nulo, un if (n <= 0) && (n != null) es imprescindible.

    Yo hubiera utilizado una colección, que funcionan mejor.

    Muchas gracias por compartir código.

    Feliz año nuevo.

  • Juan, en java int es un tipo primitivo y no puede ser null, por lo que no aplica esa validación 🙂

  • Yo haría una clase ArabicRomanConversionsTable, y me llevaría a esa clase el método findBiggestNumeralThatFit. Que además no devolvería un entero devolvería una ArabicRomanConversion que es un value object. el código quedaría algo así:

    Conversion conversion = conversionTable.findBiggestConversionFor(theArabicNumberToTransform);
    return conversion.romanRepresentation() + to_roman(theArabicNumberToTransform – conversion.arabicRepresentation);

    y no uso stringbuffer porque lo que gano en optimizacion no compensa lo que pierdo en fluidez y legibilidad.

    • jmbeas

      Alfredo, simple, claro y elegante, como siempre.
      Feliz año también para vosotros.
      Un abrazo

  • Por cierto, que maleducado soy!, feliz año MMXI!!

  • Pingback: Tweets that mention Se hace camino...] : Simetría -- Topsy.com()

  • Como no es un post navideño ahi va un comment navideño:

    Feliz 2011 y que todo lo que en mente tengas preparado para llevar acabo se convierta en realidad. Sigue luchando por tus ideales y convicciones y si necesitas algo ya sabes donde están los amigos.

    Ya me explayé en mi post de objetivos de 2011 y poco voy a añadir aqui.

    Un abrazo Jose.

    PD: espero no haber cometido ninguna falta, que aunque soy de Valladolid no se me da bien el teclado 😉

  • Mmm esa separacion entre el algoritmo y la estructura de datos me huele a cierta distancia con la oop mas “pura” (que seria algo como lo que Alfredo a propuesto). Ademas con_esos_s en lugar del CamelCase y usar tipos primitivos tiene cierta pinta de c, lo que a mi al menos me mola. Tambien lo de usar la recursion 🙂
    Como soy un pesado no puedo dejar de pegar el link a una solucion en clojure (algo mejorable) que se parece bastante (por el fragmento de codigo) a la solucion de JM 😉

  • oops, feliz año 2011 (ya sabeis que es numero primo y la suma de 11 numeros primos consecutivos MUHAHAHA toma ya frikisimetria) y el link: http://rosettacode.org/wiki/Roman_numerals#Clojure