Refinamiento Sucesivo

Si te mostrara ahora un trozo de código y te dijese: “Mira que limpio está” podrías pensar que esto ha salido a la primera de mi cabeza porque soy  muy bueno pero no es así. Cuando escribes un código la mayoría de veces te  pasará que empiezas a hacer cosas “porque funcionan” sin pensar mucho  en las buenas prácticas sobretodo cuando estás empezando con ellas. Pero no por eso debes dejarlo pasar.

Continue reading

Concurrencia

Concurrencia… Aaaah mi amiga la concurrencia, como te quiero y te odio a la vez. Tú junto con la recursividad has sido, de momento, los dos conceptos  que más me ha costado entender en la informática. Es de esas  cosas que o entiendes a la primera o tardas tiempo hasta que un día, hace  “click” en tú cabeza y todo cobra sentido, vamos a por ti ¿eres realmente tan  necesaria?

Continue reading

Las 4 reglas de un buen diseño

Aparición

¿Que pensarías si te contara que existen cuatro simples reglas que te ayudaran a crear buenos diseños según trabajas? Cuatro reglas con las que aparece el buen diseño por si solo y estas son las cuatro reglas de Kent Beck del Diseño Simple, estas las deben de cumplir nuestra aplicación:

  • Pasar todos los test
  • No debe tener duplicidad
  • Expresar la intención del programador
  • Minimizar el número de clases y métodos

Están ordenadas por importancia de las mismas.

1. Pasar todos los test

Lo primero y principal tiene que ser una aplicación testable, esto es importante y creo que sabes porque pero te lo recuerdo. Si nuestro sistema es testable 100% sabremos rápidamente cuando falla y donde falla gracias a los test (si los ejecutamos cada vez, obviamente). Para que nuestro sistema sea testable nos será más sencillo si respetamos el principio de responsabilidad única del que ya hemos hablado en anteriores ocasiones y si usamos técnicas como TDD que se basa en hacer test antes de la propia implementación. Por ello algo tan simple, a primera vista, como que nuestra aplicación tiene que estar testada de principio a final y ejecutar estos test continuamente hará que sea una aplicación más cohesiva y menos acoplada, ambos parte del objetivo principal de la orientación a objetos.

2-3. Refactorizar

Por cada pocas líneas que escribamos debemos pararnos a pensar si realmente se pueden escribir o estructurar mejor, si hay expresiones que extraer como métodos para que sea más legible, si tenemos variables mal nombradas, etc. Esto es mucho más cómodo si tenemos test que nos aseguran que tras cada cambio todo sigue funcionando correctamente. Este paso es bastante importante ya que de primeras podemos darle una solución a una implementación según nos vienen a la cabeza para que pase nuestro test, pero luego, en frío vemos lo que acabamos de escribir y vemos con mayor claridad que es mejorable, si es así, a por ello mejóralo no lo dejes por miedo!

Muchas veces mientras escribimos nuestra solución estamos tan metidos en el problema y estás tan metido en él que comienzas a escribir trozos de código duplicado sin pensarlo, pones nombres a variables con prisas porque solo quieres ver ya después de horas ver ese test en verde y escribes líneas que en unos días te arrepentirás de no haber hecho como es debido. Así que para, lee y piensa mi yo del futuro le gustaría ver esto.

4. Minimizar clases y métodos

Esta regla está al final porque es la que menos prioridad se le da y es porque muchas veces para ayudar a que la aplicación sea más expresiva y no tenga duplicidad de código, creamos clases o métodos minúsculos añadiendo más y más clases y métodos a la aplicación. Pero no te preocupes, si lo has creado y este tiene una razón de ser es porque debe existir. Y como todo en esta vida, la experiencia te va haciendo ver cuando debes y no extraer esa clase o método. Poco a poco se aprende.

Libro original

¿Cómo construirías una ciudad?

Sistemas

En este capítulo el autor empieza con un símil que me gusta mucho y voy a tratar de traducir lo más fielmente que sé.

¿Cómo construirías una ciudad?

¿Podrías administrar todos los detalles por ti mismo? Probablemente no. Incluso administrar una ciudad es mucho para una sola persona. Sin embargo, las ciudades funcionan (la mayor parte del tiempo) Funcionan porque tienen equipos de personas que administran partes de la ciudad, el sistema de agua, etc […] Las ciudades funcionan porque tienen los niveles apropiados de abstracción y modularidad haciendo posible para las personas y “componentes” se las arreglen para trabajar efectivamente, sin entenderla a gran escala. Aunque los equipos de desarrollo a menudo se organizan así también, los sistemas en los que trabajan a menudo no tienen la misma separación de preocupaciones y niveles de abstracción. […] En este capítulo veremos como mantenerse limpio a niveles altos de abstracción, a nivel de sistema.

Y me gusta porque realmente veo así una aplicación o producto software como un ente enorme que tienes que separar en pequeñas “responsabilidades” para poder gestionarlo.

Separar la construcción de un sistema de usarlo

Una de las formas de separar la construcción del uso es mover todos los aspectos de construcción (creación de objetos) a nuestro main. De esta maneja queda un flujo limpio y fácil de comprender, creamos todos los objetos que nos hacen falta en el main y luego los enviamos a la parte de nuestra aplicación donde nos sea necesario. Es decir a nuestro main no le entra información de la aplicación solo envía hacia afuera.

Fábricas

Esto no siempre es posible, por ejemplo si tenemos una aplicación de pedidos, esta tendrá que modificar la lista de pedidos añadiendo un nuevo objeto alguna que otra vez. Para ello podemos usar el patrón Abstract Factory

abstract factory esquema

Como podemos ver nuestro cliente (main) tiene una interfaz genérica para nuestros productos (productoA) y una fábrica (AbstractFactory), de esta manera el conoce que puede crear productos mediante el uso de la fábrica pero desconoce como esta los crea ya que es una interfaz y carece de detalles de implementación. A él lo único que le interesa e importa es que puede crearlos y usarlos.

Inyección de dependencia

La inyección de dependenciaconsiste, como su nombre indica, en añadir, insertar dependencia a un objeto. Esto no es recomendable porque creas una dependencia grande entre objetos pero en ocasiones es la mejor solución que tenemos para asegurarnos de que el objeto B que usamos dentro del objeto A es el que queremos y como lo queremos.

Una interfaz puede ser algo como IDisposable, IEnumerable o IPrintable. Una clase es una implementación real de una o más de estas interfaces: List o Map pueden ser implementaciones de IEnumerable.

Para entendernos: a menudo tus clases dependen unas de otras. P.ej. podría tener una clase database que acceda a su base de datos, pero también desea que esta clase inicie sesión para acceder a la base de datos. Supongamos que tiene un registrador (logger) entonces la base de datos tiene una dependencia al registrador.

Hasta aquí todo bien.

Puede modelar esta dependencia dentro de su clase de Base de Datos con la siguiente línea:

var logger = new Logger ();

y todo está bien Está bien hasta el día en que se da cuenta de que necesita un grupo de registradores: a veces desea iniciar sesión en la consola, a veces en el sistema de archivos, a veces usando TCP / IP y un servidor de registro remoto, y así sucesivamente …

Y, por supuesto, NO quieres cambiar todo tu código (mientras tanto, tienes miles de millones) y reemplazar todas las líneas

var logger = new Logger ();

por:

var logger = new TcpLogger ();

Primero, esto no es divertido. En segundo lugar, esto es propenso a errores. Tercero, este es un trabajo estúpido y repetitivo para un mono entrenado. Entonces, ¿Qué haces?

Obviamente, es una buena idea introducir una interfaz ICanLog (o similar) que implementan todos los registradores. Así que el paso 1 en tu código es lo que haces:

ICanLog logger = new Logger ();

Ahora que la inferencia de tipo ya no cambia de tipo, siempre tienes una única interfaz contra la cual desarrollar. El siguiente paso es que no desee tener un nuevo Logger() una y otra vez. Así que pones la fiabilidad para crear instancias nuevas en una única clase central de fábrica y obtienes un código como:

ICanLog logger = LoggerFactory.Create ();

La fábrica misma decide qué tipo de registrador crear. A su código ya no le importa, y si desea cambiar el tipo de registrador que está utilizando, cámbielo una vez: Dentro de la fábrica.

De esta manera con la interfaz te aseguras que todas las implementaciones de registrador, sea cual sea actúen de la misma forma ya que implementan la misma interfaz. Esta última último tramo de la publicación es una traducción de una respuesta en StackOverflow la respuesta completa aquí

Libro origina

Estructurar clases (Java)

Clases

Hasta ahora hemos visto como escribir de la mejor manera posible lineas y bloques de código, pero aun no hemos visto como hacerlo con las clases.

Ordenar los métodos

A la hora de organizar las clases es importante que si tenemos un método que hace uso de otro que tenemos en nuestra clase, es mejor si lo ponemos a continuación, de manera que si estamos leyendo el código no tengamos que dar saltos en él y sea lo más parecido a un artículo.

Modificadores

Nuestras variables, métodos o funcionalidades deben tener el modificador más restrictivo posible, si tenemos que acceder a un método en un test y no podemos por ser privado lo pondremos como protected, de manera que será accesible desde el mismo paquete.
Principio de responsabilidad única

Un punto importante a la hora de desarrollar un clase es el tamaño porque, el tamaño si importa, al igual que con lo métodos las clases tienen que ser lo más pequeñas posibles y en lo que a dimensiones se refiere no hay un máximo de líneas. En su lugar lo que hay que tener en cuenta es el número de responsabilidades, una clase no debe tener más de una responsabilidad si se te pasa por la cabeza añadirle otra (consejo, probablemente esto sea una nueva clase). Y esto el principio más importante en la Orientación a Objetos, el principio de responsabilidad única (SRP) y en resumidas cuentas dice que una clase debe tener un solo cometido, de manera que si cambia sea por una única razón. Evitando entremezclar términos de lógica de negocio, con persistencia, con interfaz de usuario.

Cohesión

Es importante que las clases tengan el menor número posible de instancias de variables y además, para lograr mayor cohesión estas variables tienen que ser accedidas por el mayor número de métodos de la clase. Se dice que una clase es cohesiva cuando cada método de esta accede a cada variable instanciada. En general es complicado alcanzar estos niveles de cohesión. Por ejemplo la siguiente clase tiene un nivel de cohesión bastante elevado.

public class Stack {
    private int topOfStack = 0;
    List<Integer> elements = new LinkedList<Integer>();

    public int size() {
        return topOfStack;
    }

    public void push(int element) {
        topOfStack++;
        elements.add(element);
    }

    public int pop() throws PoppedWhenEmpty {
        if (topOfStack == 0)
            throw new PoppedWhenEmpty();
        int element = elements.get(--topOfStack);
        elements.remove(topOfStack);
        return element;
    }
}

Como se ve tienen tan solo dos variables instanciadas y sólo en un método no se accede a ambas (size).

Libro original

 

Test Unitarios

Test Unitarios

La programación a evolucionado mucho en tan solo 20 años, antes por 1997 cuando estaba empezando no había forma de probar las funciones que creábamos. No era raro crear la función añadirle una parte en la que podamos introducir por teclado caracteres o alguna orden y ver como responde este, pero a día de hoy existen los test unitarios. Test que ejecutarán nuestra función y comprobarán si el resultado obtenido es el que nosotros esperábamos. Tan buenos son estos test y tanto ruido han hecho las metodologías ágiles y el TDD que no es raro a día de hoy que en una entrevista de trabajo te pregunten explícitamente por ellos.

Las tres leyes del TDD

  1. No escribirás código de producción hasta que exista un test que falle.
  2. No escribirás más de un test unitario que falle.
  3. No escribirás más código de producción del necesario para pasar el test.

Estas reglas se convierten en un ciclo de trabajo que no dura más de 30 segundos. Escribes un test, compruebas que falle, escribes el código para que este no falle. De esta manera escribimos los test y el código que lo pase al mismo tiempo. Si tomamos este hábito será fácil escribir docenas de test diariamente lo que hará que cada mes tu software tenga cientos de test nuevos que lo prueban ¿quien no quiere eso?

Mantener los test limpios

Puede que pienses que los test son una simple herramienta para llegar a un código de producción sólido y robusto, que realmente el fin es este código de producción. Y por ello no le prestes mucha importancia al como realizar los test, los creas con “prisa” dejas variables o los propios test con malos nombres. El problema vendrá en el futuro cuando creando un nuevo test para una nueva funcionalidad tienes que cambiar algo del código y bum explotan unos cuantos test a parte del nuevo. Comienzas a leer y ves que no tienes idea de que están probando realmente, en ese momento comenzará un viaje el cual (con suerte) si has escrito el test hace poco y tienes buena memoria acaba rápido pero en la mayoría de casos no será así.

Con todo esto quiero decir que el código de los test es tan importante como el de producción.

Un test limpio

¿Como mantenemos un test limpio? Legibilidad, al igual que hemos hablado de como hacer un código limpio los test funcionan de un modo similar: claridad, simplicidad y densidad. Imagina que te enfrentas a este test:

**public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks()
throws Exception{
    WikiPage pageOne = crawler.addPage(root, PathParser.parse("PageOne"));
    crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
    crawler.addPage(root, PathParser.parse("PageTwo"));

    PageData data = pageOne.getData();
    WikiPageProperties properties = data.getProperties();
    WikiPageProperty symLinks = properties.set(SymbolicPage.PROPERTY_NAME);
    symLinks.set("SymPage", "PageTwo");
    pageOne.commit(data);

    request.setResource("root");
    request.addInput("type", "pages");
    Responder responder = new SerializedPageResponder();
    SimpleResponse response =
(SimpleResponse) responder.makeResponse(
        new FitNesseContext(root), request);
    String xml = response.getContent();
    assertEquals("text/xml", response.getContentType());
    assertSubString("<name>PageOne</name>", xml);
    assertSubString("<name>PageTwo</name>", xml);
    assertSubString("<name>ChildOne</name>", xml);
    assertNotSubString("SymPage", xml);
}

Puede que logres comprenderlo después de un tiempo de análisis, no digo que sea imposible. Incluso puede que no te lleve mucho tiempo, pero enfrentarse a test así diariamente acaba quemando antes nuestra cabeza. Si en lugar de eso tenemos ese mismo test pero de la siguiente forma:

public void testSymbolicLinksAreNotInXmlPageHierarchy() throws Exception {
    WikiPage page = makePage("PageOne");
    makePages("PageOne.ChildOne", "PageTwo");

    addLinkTo(page, "PageTwo", "SymPage");

    submitRequest("root", "type:pages");

    assertResponseIsXML();
    assertResponseContains(
        "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>"
    );
    assertResponseDoesNotContain("SymPage");
}

Sigue teniendo las mismas tres partes diferenciadas. Una primera parte donde creas los datos para el test, a continuación donde utilizamos estos datos y finalmente comprobamos si el resultado es el esperado.

Un solo assert por test

Una buena mentalidad es la de un solo assert por test, en la práctica no es tan fácil y veremos que aveces tendremos que poner más de uno. Es una mentalidad que se suele tener en cuenta pero no es posible llevarla a cabo en todos los casos. Por ello mejor el mínimo de asserts por test.

Lo que realmente tiene que ser único por test es el concepto a probar, que es lo que queremos poner a prueba y eliminar todo el ruido que pueda generarse a su alrededor. Con esto me refiero, si tienes una función que puede generar tres casos, por ejemplo. Dado un número este puede ser mayor, menor o igual. No haremos un test que pruebe estos tres casos de una sola vez, tenemos que hacer tres test independientes de manera que cada uno de ellos prueba un concepto, un caso.

F.I.R.S.T.

Las cinco reglas que siguen los test limpios:

  • Fast, rápido: un test tiene que ejecutarse rápido, si tenemos un test que tarda bastante en completarse no nos gustará ejecutarlo con frecuencia y tardaremos más en ver posibles errores.
  • Independent, independiente: los test tienen que ser independientes unos de otros, no podemos tener test que están conectados entre ellos porque en el momento que el primero falle todos los demás irán detrás, y será complicado saber donde empieza el problema.
  • Repeatable, repetible: los test tienen que poder ser ejecutados en cualquier ambiente (enviroment) desde producción hasta tu portátil en casa sin conexión a internet.
  • Self-validating, auto-validados: los test tienen que, por si mismos, dar como resultado un boolean (verdadero o falso) no queremos test que guarden un resultado en un documento de texto que tendremos que mirar a posterior y comprobar si coincide con otro que ya tenemos, Somos humanos y nos equivocamos, podemos no comprobar bien estos resultados y más cuando llevamos ya unas cuantas horas de trabajo encima ese día.
  • Timely, oportuno: los test solo serán escritos antes del código de producción que haga que los pase. Si tratas de escribirlos después acabarás odiando los test, porque habrá partes que son muy complicadas de probar o el código de producción que tienes no está si quiera pensado para ser probado.

Libro original

Referencias