Mejorando la kata FizzBuzz

Hace unos días hicimos nuestro primer video haciendo la kata FizzBuzz, esto fue un primer acercamiento de como empezar con TDD y buenas prácticas. Ahora reflexionamos sobre la solución de la kata y veremos que introducimos ciertos code smells que en una segunda iteración intentaremos ir solucionándolos para hacer llegar a un código más robusto.

Enunciado de FizzBuzz

Escribe un programa que imprima los números del 1 al 100, pero aplicando las siguientes normas:
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.

fizzbuzz.png
FizzBuzz in CSS

El código de la primera iteración es este.

En esta iteración vimos como resolver el problema, pero aunque nuestra solución hace lo que pide el enunciado de la kata hay ciertos code smells en el código que podemos mejorar.

Mi solución

En esta segunda iteración que podemos ver en Github) vemos el código separado en clases, intentando mejorar todo lo que hicimos anteriormente, basándonos en los tests que tenemos iremos comentando cada una de los pasos que hemos tomado para llegar a la solución de FizzBuzz.

class FizzBuzz
{
    const TOTAL = 100;
    const INITIAL = 1;

    public function __construct(RuleMachine $ruleMachine)
    {
        $this->ruleMachine = $ruleMachine;
        $this->ruleMachine->add(new BuzzRule());
        $this->ruleMachine->add(new FizzRule());
        $this->ruleMachine->add(new FizzBuzzRule());
    }

    public function getData()
    {
        $numbers = [];
        for ($i = self::INITIAL; $i <= self::TOTAL; $i++) {
            $numbers[$i] = $this->ruleMachine->generateValue($i);
        }
        return $numbers;
    }

}

El código está totalmente acoplado

No se cumplen los principios SOLID (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion), está todo el código en una sola clase, por lo que el principio de única responsabilidad brilla por su ausencia.

Solución

Vamos a crear una serie de clases, para ello empezamos separando FizzBuzz que es la clase que exponemos al exterior que generará los 100 números, de la clase FizzBuzzRuleMachine que será una clase que dado un número aplicará una serie de reglas para generar un valor.

Magic Numbers

Para generar los 100 valores de FizzBuzz que requiere el enunciado no podemos hacer un bucle de 1 a 100, ya que las reglas de negocio (generar 100 valores) deben quedar separadas para que cuando alguien lea “de nuevas” el código llegue a saber que el 100 es algo importante.

Solución

Introducimos las constantes TOTAL e INITIAL para resaltar que dichos valores son importantes.

Seguimos aplicando SOLID

Hemos separado el código de FizzBuzzMachine el aplicar reglas de las Rules propiamente dichas. Cada Rule tiene un método check que evalúa si el valor de entrada cumple la regla y el método generateValue que es el valor esperado.

Utilizar interfaces

Aunque parezca una tontería en un ejemplo tan básico, utilizar interfaces es un buen entrenamiento para el código de producción. Una interfaz es una manera común de usar cierto tipo de objetos, por ejemplo todos esperamos que los coches tengan un volante, unos pedales de aceleración y freno independientemente de la marca y modelo del coche.

Nosotros hemos creado interfaces para las Rule

interface Rule
{
    /**
     * @param $number
     * @return bool
     */
    public function check($number);

    /**
     * @return String
     */
    public function generateValue();
}

interface RuleMachine
{
    /**
     * @param $i
     * @return String
     */
    public function generateValue($i);

    /**
     * @param Rule $rule
     */
    public function add(Rule $rule);
}

Cómo seguir mejorando nuestro código

Seguramente encontraréis muchos peros y quizás incluso fallos en el código, todo el código es mejorable. Aquí podría haber hecho más tests para cubrir todas las clases, o mejorar los tests para que no fuesen tan acoplados al código.

La mejor manera de mejorar nuestro código es leyendo y practicando.

Leyendo libros como The Software Craftsman: Professionalism, Pragmatism, Pride (Robert C. Martin), Clean code, Refactoring o Head First Design Patterns y practicando, ya sea haciendo katas, haciendo pairing, leyendo código de otros, utilizando herramientas que nos ayuden a ir mejorando poco a poco, como por ejemplo PHP Code Sniffer y PHP Mess Detector.

Utilizando “PHP Code Sniffer” y “PHP Mess Detector”

“PHP Code Sniffer” es un “espía de nuestro código en php” para determinar si cumple con determinadas reglas de estilos y “PHP Mess Detector” es una herramienta automática para detectar código no utilizado, nombres de variables poco apropiados, etc. basándonos en un conjunto de reglas.

Para instalarlo, lo más sencillo es utilizando composer

composer global require "squizlabs/php_codesniffer=*"
composer global require "phpmd/phpmd"

Configurarlo dentro de PHPStorm es muy sencillo. Indicamos donde estan instalados y listo.
configuración phpcs y phpmd

Para que PHPStorm nos resalte las reglas solo tenemos que añadirlo a al configuración:
configuracion phpstorm

Con esto conseguiremos que PHPStorm nos indique cuando estamos haciendo cosas un poco “raras”.

Conclusiones

La mejor manera de mejorar nuestro código es leyendo y practicando. Seguramente haya olores en mi código que no he detectado y se me han pasado por alto ¿cuáles son? ¿como mejorarías la kata? ¿qué libros me recomiendas?

Anuncios

Comenta la entrada

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s