Screencast de la kata FizzBuzz en Javascript

Esta no es la entrada que tenía pensada, pero algunas personas me han pedido que haga las entradas relativas a katas mediante screencasts, por lo que dicho y hecho. He repetido la kata FizzBuzz en Javascript como prueba, ya que no domino mucho el arte de los screencasts.

En esta kata he seleccionado Javascript como lenguaje de programación y Jasmine como framework de tests. Hay que recordar que soy principiante con Javascript por lo que pueden existir detalles que se pueden implementar mejor de alguna otra manera, así que cualquier comentario será bienvenido.



Aunque he intentado que todo se vea lo más claro posible, te recomiendo que veas el vídeo en HD en la propia página de Vimeo, haciendo click sobre el enlace que aparece cuando te posiciones sobre el vídeo, o incluso que descargues el fichero.

Después de este pequeño aporte al blog seguiré trabajando en una entrada sobre frameworks para desarrollo de aplicaciones ... stay tuned! como diría David Bonilla :-)


Kata FizzBuzz: Javascript, BDD y APIs fluidas

Durante el día de hoy he tenido algo de tiempo libre y he decidido hacer la Kata FizzBuzz. Algunos os preguntareis, ¿y qué es es eso de la Kata FizzBuzz?, es más ¿qué es una Kata?. ¿Eso no es algo de Karate?. Pues sí, en muchas artes marciales se practican Katas para interiorizar una serie de movimientos preestablecidos, es decir que los aprendamos de manera tan automática que salgan solos. En el desarrollo de software se puede hacer lo mismo practicando con algunos problemas sencillos, lo importante no es resolver el problema en si mismo sino interiorizar una serie de buenas prácticas mientras lo resolvemos (TDD, refactorización, etc).

La Kata FizzBuzz en concreto es un problema MUY sencillo, que se puede ver en este enlace. A grandes rasgos lo único que hay que hacer es crear una programa que dado un número se comporte de la siguiente manera.

  • Devuelve fizz si el número es divisible por 3.
  • Devuelve buzz si el número es divisible por 5.
  • Devuelve fizzbuzz si el número es divisible por 3 y por 5.

Hay otras variantes en las que se pide escribir un programa que te devuelva el resultado para los primeros 100 número enteros, pero nos quedaremos con lo que he explicado anteriormente, porque esa variante no añade mucho valor a lo que queremos aprender.

Para resolver el problema yo he seleccionado Javascript como lenguaje de programación, ¿por qué?, pues porque es una herramienta que cada vez tengo que utilizar más y no domino ni lo más mínimo, incluso se puede decir que no tengo ni idea de Javascript, por eso en mi caso es un lenguaje interesante para hacer una Kata. Como framework de test en Javascript he elegido Jasmine porque ya he usado Qunit anteriormente y quería utilizar una aproximación más BDD (no explicaré como funciona ni como instalarlo porque en la web ya lo hacen maravillosamente, si alguien tiene dudas por supuesto puede preguntar).

Para los que no lo sepan, BDD son las siglas de Behaviour Driven Development. Yo soy de esos desarrolladores que piensan que TDD y BDD son el mismo perro con distinto collar, solo cambia la forma en la que enuncias los test, pero en la práctica no cambia mucho los pasos que realizas (siempre depende de cada desarrollador), por no decir que no cambian nada. Jasmine es un framework BDD porque enuncia los tests como Specs, es decir que en vez de llamarse test utiliza la construcción describe-it al estilo de RSpec. Puedes comprobarlo en mi solución, que llegó a las siguientes especificaciones.



describe("FizzBuzzCalculator", function() {

it("should return 0 with input 0", function() {
expect(FizzBuzzCalculator.calculate(0)).toEqual(0);
});

it("should return fizz with input 3", function() {
expect(FizzBuzzCalculator.calculate(3)).toEqual('fizz');
});

it("should return buzz with input 5", function() {
expect(FizzBuzzCalculator.calculate(5)).toEqual('buzz');
});

it("should return fizz with input 6", function() {
expect(FizzBuzzCalculator.calculate(6)).toEqual('fizz');
});

it("should return input when input is not divisible by 3 or 5 (2 == 2)", function() {
expect(FizzBuzzCalculator.calculate(2)).toEqual(2);
});

it("should return buzz with input 10", function() {
expect(FizzBuzzCalculator.calculate(10)).toEqual('buzz');
});

it("should return fizzbuzz with input 15", function() {
expect(FizzBuzzCalculator.calculate(15)).toEqual('fizzbuzz');
});

it("should return fizzbuzz with input 30", function() {
expect(FizzBuzzCalculator.calculate(30)).toEqual('fizzbuzz');
});

});


Una de las cosas que se pueden observar con estos tests es que he intentado ir muy poco a poco, triangulando en todos los casos. ¿Qué es eso de triangular?, muy sencillo, cuando me encontraba por ejemplo con el caso "should return fizz with input 3", en la implementación hacía que devolviese 3 de forma directa, y luego creaba otro test "should return fizz with input 6" que me llevaba a "descubrir" la función isDivisibleByThree.

Si te fijas, el primer test que realicé fue la comprobación que devuelve cero ante la entrada cero, pero luego seguí por la entrada tres que debe devolver fizz. No fue hasta el cuarto test cuando me di cuenta que si el número no era divisible por 3 ni por 5 debía devolver el propio número. Hasta ese momento solo devolvía cero para todos los casos. He ahí la importancia de tener una libreta con los tests que se deben pasar y anotar todos los nuevos tests que vayan apareciendo. Yo anote dicho test y continué hasta llegar a una solución que me convencía.



FizzBuzzCalculator =  {

calculate : function(input) {
if (input == 0) return 0;
if (isDivisibleByTrhee(input) && isDivisibleByFive(input)) return 'fizzbuzz';
if (isDivisibleByThree(input)) return 'fizz';
if (isDivisibleByFive(input)) return 'buzz';

return input;
}

function isDivisibleByTrhee(input) {
return input % 3 == 0;
}

function isDivisibleByFive(input) {
return input % 5 == 0;
}

}


Buscando lo que había hecho otra gente encontré un enlace al blog de Javier Acero, recomiendo la lectura porque comenta la importancia del diseño orientado a objetos y se ve como extrae las comprobaciones de la divisibilidad a otra clase. Pues claro!!! se me tenía que haber pasado a mi por la cabeza. Esos son el tipo de detalles que hay que tener en cuenta en una Kata. Se me ocurrió que era buen momento para aprender a hacer APIs fluidas en Javascript y llegue a esta nueva solución, comprobando que pasaba todos los tests que ya pasaban antes.



var number = function(val) {

var operations = {
_val: val,

isDivisibleBy: function(divisor) {
return this._val % divisor == 0;
}
}

return operations;
}

FizzBuzzCalculator =  {

calculate : function(input) {
if (input == 0) return 0;
if (number(input).isDivisibleBy(3) && number(input).isDivisibleBy(5)) return 'fizzbuzz';
if (number(input).isDivisibleBy(3)) return 'fizz';
if (number(input).isDivisibleBy(5)) return 'buzz';

return input;
}

}


Quizás la "clase" number no es tan clara como se espera (puede que sea por mi ignorancia en Javascript), pero desde luego de cara a los usuarios de nuestra API tener líneas como



if (number(input).isDivisibleBy(5)) return 'buzz'


es más claro que el agua :-)

Bueno no he explicado lo que es una API fluida, ¿no lo has averiguado tú solo?, pues se trata de una API en la que se componen las llamadas a los métodos de manera consecutiva, es decir llamamos a number(input) y seguidamente podemos llamar a isDivisibleBy(5) de forma que se puede leer todo junto y se entiende que se quiere comprobar que es input el que tiene que ser divisible por 5 :-D ¿Está más claro ahora? Por ejemplo en Java, JodaTime es un buen ejemplo de API fluida.



public boolean isRentalOverdue(DateTime datetimeRented) {
  Period rentalPeriod = new Period().withDays(2).withHours(12);
  return datetimeRented.plus(rentalPeriod).isBeforeNow();
}


Espero que os haya gustado y que hagáis todas las críticas (constructivas) buenas o malas que merezca :-D el debate es lo interesante.

Rework

Hace poco tuve el placer de asistir a la AOS2011 en Pamplona, lo que también implica que tuve que pasar algunas horas entre aviones y aeropuertos. Aprovechando que este año me han regalado un Kindle, antes de iniciar el viaje rebusqué en mi "lista de deseos" de Amazon, y acabé comprando Rework. Todo el mundo habla muy bien de este libro y tenía muy buena crítica, así que pensé, ¿por qué no?. Además sería más suave de leer que otros muchos libros que tengo pendientes :-)




Desde luego fue una apuesta arriesgada (para mí) porque no es el libro típico que suelo leer. Este libro está un poco más enfocado a la "gestión de la empresa" de lo que suele ser habitual en mí, ese tipo de cosas más acordes a personas como David Bonilla :-P Pero en realidad fue una grata sorpresa porque desde el principio del libro se opone a una gran cantidad de estereotipos clásicos.

Nada más empezar, uno de los capítulos hace hincapié en que no es necesario crecer para formar una gran empresa. Todos tenemos la idea preconcebida de que para que una empresa gane dinero tiene que crecer, pues bien los autores de este libro te contarán como eso es falso (siempre en función de lo que tú mismo consideres qué es ganar dinero). Ohhh sí, los autores, ya se me estaba pasando ... son Jason Fried y David Heinemeier Hansson. ¿No te suenan de nada?, pues son los fundadores de 37signals, entre otras cosas que dejaré que descubras tu mismo. ¿Te siguen sin sonar de nada? pues ve a Google, navega un poco y después vuelves, no te lo voy a dar todo hecho :-) Vaya, resulta que los autores sí son empresarios de éxito por lo que sus experiencias tendrán alguna base sostenible.

Cuando crees que ya no te pueden sorprender más, aparecen capítulos en los que te enseñan como tienes que decirle "no" al cliente, no de vez en cuando sino siempre, desde el principio la primera respuesta es "no", luego se puede transformar en un "ya veremos". Parece muy radical, pero no te dejes llevar por mi breve resumen, la explicación de este tipo de pensamientos es totalmente brillante y simple. Pura lógica aplastante :-)

Al final me ha terminado gustando bastante, porque me ha enseñado que muchos de los puntos de vista que yo mismo aplico, están basados más en mi entorno socio-cultural que en prácticas o reglas que realmente funcionen. A partir de ahora tendré que ser algo más crítico y aplicar muchas de las prácticas que se comentan en el libro.

Para acabar, algunas de las partes que subrayé y que me parecieron interesantes durante la lectura.
"The real world isn't a place, it's an excuse, It's a justification for not trying. It has nothing to do with you."
"Working wihtout a plan may seem scary. But blindly following a plan that has no relationship with reality is even scarier."
"Maybe the right size for your company is five people. Maybe it's forty. Maybe it's two hundred. Or maybe it's just you and a laptop."
"Sometimes abandoning what you're working on is the right move, even if you've alredy put in a lot of effort."
"Never hire anyone to do a job until you've tried to do it yourself first. That way, you'll understand the nature of the work."
"You can't install a culture. Like a fine scotch, you've got to give it time to develop." 
Esa última es una de mis favoritas, y creo que lo dejaré aquí porque sino me van a terminar acusando de plagio :-) Si no te he logrado convencer ... pues tampoco era mi intención, pero sí te diré que es un libro muy interesante y recomendable para afrontar el mundo empresarial actual.

P.D: Quiero dedicar esta entrada al gran David Bonilla y que tenga mucha suerte en su nueva aventura empresarial :-D