Archivos de la categoría diseño

Mis motivaciones con TDD y ATDD

Cuando pienso en (A)TDD me vienen a la cabeza dos cosas: simplicidad y reducción del desperdicio, dos cosas que son, en mi opinión, dos caras de la misma moneda.

El diseño de software, no importa como se lo mire, es un proceso complejo. El diseño, no importa de que cosa, es un proceso complejo. Nuestro cerebro no es capaz de evaluar todas las variables o grados de libertad en abstracto, como hacen los buenos jugadores de ajedrez tomando en cuenta muchas jugadas hacia adelante.

Es por esto que necesitamos concretar, eliminar grados de libertad, probar ideas.

¿Como reduce el desperdicio (A)TDD?

Evitando dedicar tiempo a construir (y probar, depurar, mantener, etc.) cosas que no necesitamos, utilizando el proceso:

    1. analizo las necesidades
    2. pienso como resolverlas
    3. decido como probar lo que construiré, con ejemplos concretos
    4. escribo una prueba que falle para el ejemplo mas simple
    5. escribo el código mas simple posible que hace pasar la prueba
    6. aprovecho oportunidades para mejorar mi código y pruebas

Veo este proceso como una suerte de embudo que me obliga a trabajar solo en lo que necesito, por ejemplo:

    • en el paso 5 escribo solo el código necesario para pasar el test
    • en el paso 4 escribo la prueba basada en el el ejemplo concreto mas simple sin resolver
    • en el paso 3 pienso en ejemplos concretos de lo que quiero lograr (no en “posibles” necesidades abstractas)
    • en el paso 2 dedico unos minutos a pensar como resolveré las funcionalidades (no comienzo a programar ciegamente)
    • en el paso 1 dedico un tiempo a entender el contexto de lo que debo hacer

Este proceso respeta dos cuestiones transversales muy importantes: se repite en ciclos cortos y la calidad no es opcional (calidad es arquitectura, rendimiento, cumplir con estándares, documentación, etc.)

 ¿La simplicidad?

Encuentro la simplicidad en los mismos puntos anteriores pues no construyo nada que no sea necesario por un paso en el proceso que mencioné antes, sumando al combo al refactoring, esa simplificación de mis artefactos que me permita no solo sostener y mejorar el ritmo de trabajo a lo largo del proyecto.

¡Nos vemos!

TDD no es prueba, es diseño.

En una de las últimas sesiones de coaching en la cual ayudé a un equipo a experimentar TDD, escuché una de las reacciones mas usuales ante el primer paso.

Para definir el contexto, imaginen que van a escribir la famosa kata tenis y que aun no han escrito absolutamente nada de código. Una de las posibilidades es escribir esta prueba:

[Test]
public void ShouldProvideTheScorerTitle() {
var scorer = new Scorer("Federer", "Nadal");
Assert.AreEqual("Federer vs. Nadal", scorer.Title());
}

Y una de las posibilidades para el código mas simple posible que hace pasar ese test, es:

public class Scorer {
public Scorer(string jugador1, string jugador2) {
}

public string Title() {
return "Federer vs. Nadal";
}
}

La reacciones suelen ser: ¿Por qué debemos escribir ese código tan tonto ahora? ¿Por qué no creamos directamente las variables de instancia para los nombres de los dos jugadores? ¿Qué sentido tiene devolver ese texto fijo? ¡Esa prueba no tiene sentido!

Mi respuesta usual es: No tiene sentido porque no es una prueba, es una actividad, un paso, una canal por donde fluye el diseño.

¿Qué tenemos antes de empezar? Nada concreto, solo una idea de lo que tenemos que hacer y alguna estrategia de diseño que debemos aún desarrollar, entender.

Luego del primer paso tenemos: el nombre de la clase, decidimos que pasaremos los nombres de los jugadores en el constructor, que basta con pasar los nombres como string, que usaremos un método para devolver en título y que ese método retornará un string.

Lo mas importante es que tenemos todas estas decisiones antes de escribir el código de producción, solo habiendo escrito la prueba.

¿No les parece que hemos diseñado mucho en un solo paso como para preocuparnos por detalles de implementación?

Por ejemplo, ¿es razonable que un músico se obligue a escribir música de manera que cada paso tenga sentido en la versión final? Por supuesto que no, empieza a jugar con notas y melodías (a partir de alguna idea) y luego va desarrollando, en forma iterativa la versión final.

A medida que va avanzando entiende mejor como se ajustan todos los bloques, la idea va tomando forma, va entendiendo que es lo que quiere la pieza musical.

Hay otros motivos importantes por los cuales debemos escribir el código mas sencillo posible, pero hay mucho material sobre esos motivos en la red y, en todo caso, puede ser materia de una futura entrada.

Creo un requisito importante para lograr una buena práctica de TDD es entender e incorporar la idea de que no es una técnica de prueba sino una manera de diseñar software.

Nos vemos.

La magia de TDD

Nos encontrábamos ayer en las oficinas de Kleer, en un Yoseki Codig Dojo, exclusivo para un equipo de un cliente, trabajando con la Kata CheckOut.

Quiero contarles solo 3 minutos donde se notan las ventajas de diseñar nuestro código a partir del ejemplo.

Trabajamos en Java con Eclipse pues este es el entorno de desarrollo en el cual el equipo trabaja. Les presento el código al comienzo de los 3 minutos mágicos:

int cantA = 0, cantB = 0, cantC = 0, cantD = 0;
for(String producto: productos.split(","))
{
    if (producto.equals("A"))
        cantA++;
    else if (producto.equals("B"))
        cantB++;
    else if (producto.equals("C"))
        cantC++;
    else if (producto.equals("D"))
        cantD++;
}
return cantA*50 + cantB*30 + cantC*20 + cantD*15;

Decidimos, en ese momento, eliminar la duplicación de lógica (en la declaración de variables y en la cadena de ifs). Entonces, quien estaba al teclado dijo: “Podemos hacer una método para encapsular la lógica…” a lo cual yo respondí: “¡Adelante!”. Entonces esta persona escribió:

private ...

y sobrevino una larga pausa, luego un debate.

El problema es que hay mucho que decidir si vamos por ese camino: ¿Cuál es el tipo que retorna ese método? ¿Cuál debería ser su nombre? ¿Qué parámetros debe recibir? y, sobre todo, ¿Qué debe hacer?.

Luego de dar tiempo al debate, sugerí eliminar esa línea de código y escribir lo siguiente:

return getCantA(productos)*50 + cantB*30 + cantC*20 + cantC*15;

Noten que reemplace la variable cantA por el llamado al método getCantA(…), aun inexistente.

En ese momento quedaron muy claras las respuestas a las preguntas: el método debe devolver un entero, su nombre ya esta definido y debe sumar la cantidad de productos de tipo A. En 15 segundos quedo escrito lo siguiente:

private int getCantA(String productos)
{
    int cant = 0;
    for(String producto: productos.split(","))
    {
        if (producto.equals("A"))
            cant++;
    }
    return cant;
}

Y las pruebas unitarias pasaron y el pequeño paso en el refactoring fue exitoso.

A esto me refiero con la magia de TDD. Es muy difícil diseñar el método “desde adentro”. Es mucho mas fácil y efectivo diseñarlo “desde afuera”, es decir, desde el lugar donde lo necesitamos.

Para aquellos que estén preocupados por el final de la historia (o de este refactoring), les cuento que el último paso dejó el código en estas condiciones:

private int price(String productos)
{
    return getCant(productos, "A")*50 + 
           getCant(productos, "B")*30 + 
           getCant(productos, "C")*20 + 
           getCant(productos, "D")*15;
}
private int getCant(String productos, String codProducto)
{
    int cant=0;
    for(String letra : productos.split(","))
    {
        if(letra.equals(codProducto))
            cant++;
    }
    return cant;
}

Luego siguieron otros refactorings, por supuesto, pero eso fue luego de los 3 minutos mágicos, así que termino esta entrada aquí.

Si alguno de ustedes quiere experimentar algo parecido, no dejen de organizar coding dojos en sus lugares de trabajo, un lunes por la mañana, un viernes por la tarde, cuando sea.

Si no se sienten seguros para comenzar, participen de un Yoseki Codig Dojo en Kleer, los organizamos mensualmente en Buenos Aires, en Lima, y en otras ciudades. Y son gratuitos.

Take it easy, we have builders

As I said before, it’s a valuable experience for me to keep in touch with different development teams.

Last week we were discussing the best method to validate domain model objects. My first response to that question is: model objects should never be invalid, in the first place.

Each domain object should be fully initialized in its constructor, either by initializing default values or receiving values ​​from the constructor.

Sometimes someone says that does not appreciate consructors with 20 parameters, then I question if the object is large because one or more objects are not yet discovered.

Not to make this post too long, suppose we agree that completely initialize the object with valid values ​​in the constructor and that no public method allows us to modify the state leaving it invalid. In this case, it is never necessary to validate our object because we know that it can never be invalid.

And here we come to the practice recommended to fellow developers last week: if it’s not trivial to validate a data and, once validated, to build the object, then use a Builder. Builder’s responsibilities are:

  • Make sure you have all the necessary data to construct the object
  • Provide a friendly interface for specifying the data
  • Construct the object

The following example “fluent API”:

public class InvoiceBuilder {
    private Customer _customer;
    private DateTime _date;

    public InvoiceBuilder WithCustomer(Customer customer) {
        _customer = customer;
        return this;
    }

    public InvoiceBuilder WithDate(DateTime date) {
        _date = date;
        return this;
    }

    public Invoice Build() {
        AssertDataCompleteAndValid();
        return new Invoice(_customer, _date);
    }

    private void AssertDataCompleteAndValid() {
        if (_customer == null)
            throw new ArgumentNullException("customer");

        if (_date < DateTime.Today)
            throw new ArgumentOutOfRangeException("date");
    }
}

And this code would be used this way:

var invoice = new InvoiceBuilder().
    WithCustomer(jose).
    WithDate(DateTime.Today).
    Build();

See you!

No desesperen, tenemos los builders

Como ya he dicho antes, el estar en contacto con equipos de desarrollo me resulta una experiencia muy valiosa.

Estábamos la semana pasada discutiendo sobre el mejor método para validar objetos del modelo de dominio. Mi primera respuesta ante esa pregunta es: nunca los objetos del modelo debieran estar inválidos, en primer lugar.

Cada objeto de dominio debiera inicializarse completamente en su constructor, ya sea inicializando valores por defecto, ya sea recibiendo valores desde el inicializador.

Algunas veces alguien menciona que no aprecia los constructores con 20 parámetros a lo cual respondo con un cuestionamiento sobre si realmente ese objeto debe ser tan “grande” o si hay uno o mas objetos no descubiertos aun.

Para no hacer esta entrada demasiado larga, supongamos que acordamos que inicializamos completamente el objeto, con valores válidos en el constructor y que ningún método publico nos permite modificarlo dejándolo en estado invalido (1). En este caso, nunca es necesario validar nuestro objeto puesto que sabemos que, si esta instanciado, nunca puede ser inválido.

Y aquí llegamos a la práctica que recomendé a unos colegas la semana pasada: si no es trivial validar una serie de datos y, una vez validados, construir el objeto, entonces usen un Builder. Las responsabilidades del Builder son varias:

  • Asegurarse de tener todos los datos necesarios para construir el objeto
  • Proporcionar una interfaz amigable para la especificación de todos los datos
  • Construir el objeto

A continuación ejemplo con “fluent API”:

public class FacturaBuilder {
    private Cliente _cliente;
    private DateTime _fecha;

    public FacturaBuilder ConCliente(Cliente cliente) {
        _cliente = cliente;
        return this;
    }

    public FacturaBuilder ConFecha(DateTime fecha) {
        _fecha = fecha;
        return this;
    }

    public Factura Build() {
        AssertDatosSuficientesYCorrectos();
        return new Factura(_cliente, _fecha);
    }

    private void AssertDatosSuficientesYCorrectos() {
        if (_cliente == null)
            throw new ArgumentNullException("cliente");

        if (_fecha < DateTime.Today)
            throw new ArgumentOutOfRangeException("fecha");
    }
}

Y este código se usaría de esta manera:

var factura = new FacturaBuilder().
    ConCliente(jose).
    ConFecha(DateTime.Today).
    Build();

Nos vemos!

¿Cuál es la diferencia entre el patrón strategy y state?

El jueves pasado estuvimos viendo temas de polimorfismo en el curso Elementos avanzados de arquitectura de software con objetos y, al cabo de un ejemplo, yo plantee que lo que habíamos visto era el patrón State. Hernán me puso cara de “no tanto” y sospecho que en próximas clases entenderé el porqué.

Sin embargo surgió un mini debate con unos de mis compañeros de curso: el argumentaba que en realidad lo que habíamos visto era el patrón Strategy. Creo que esta es una duda común porque ambos patrones son parecidos en algunos aspectos de la implementación y en el diagrama de clases pero son conceptualmente distintos, por lo tanto me pareció bueno escribir un pequeño post.

El patrón State permite hacer diferentes cosas dependiendo del estado del objeto. En otras palabras, lo que cambia de acuerdo al estado es que se hace. Además, todas las posibilidades están incluidas en el código del modelo. A la hora de agregar nuevos estados y su correspondiente acción asociada basta con agregar una subclase sin tocar las demás (observando el Open-Close principle).

En cambio el patrón Strategy permite hacer lo mismo de diferentes maneras. En otras palabras, lo que cambia es como se hace. Este patrón usualmente permite que la implementación específica (la estrategia) se pueda seleccionar por configuración, por el estado de cierto objeto, etc.

Les dejo algunos links en inglés que aportan mas detalles:

http://stackoverflow.com/questions/1658192/what-is-the-difference-between-strategy-design-pattern-and-state-design-pattern

http://www.c-sharpcorner.com/UploadFile/rmcochran/strategy_state01172007114905AM/strategy_state.aspx

http://c2.com/cgi/wiki?StrategyPattern

[PDF] http://userpages.umbc.edu/~tarr/dp/lectures/StateStrategy.pdf

Volviendo al preescolar en diseño orientado a objetos

Ayer comencé el curso Elementos avanzados de arquitectura de software con objetos de 10Pines, a cargo de Hernán Wilkinson (@HernanWilkinson).

Intentaré contar mis vivencias luego de cada una de las 6 clases, sin mucho análisis, en su versión cruda. Luego vendrá el tiempo de las conclusiones.

Antes de comenzar el curso estuve releyendo algún material relevante sobre diseño orientado a objetos (DOO), sobre todo el libro Smalltalk, Object and Desing, de Chamond Liu. Recomiendo este libro que, a pesar de su apariencia sencilla, no tiene desperdicio, una ganga por USD 24.

Vuelvo al curso. Es evidente que Hernán tiene mucha experiencia enseñando “objetos” ya que nos mantuvo interesados durante cuatro horas con tres simples conceptos: objetos, colaboración y mensajes, todo esto sin tocar las computadoras.

Lo primero que nos pidió fue que, momentáneamente, nos olvidáramos de todo lo que sabíamos de diseño orientado a objetos (DOO). Creo que es el enfoque apropiado para el curso ya que es usual que tengamos muchos conceptos mal aprendidos y eso dificultaría bastante el aprendizaje.

Aquellos con los que he hablado del tema sabrán que hace varios años hice mi “click” y comencé a dar el próximo paso en DOO, empezando a prestar mas atención al comportamiento en los objetos y menos al estado.

Ese fue el eje de la clase de ayer, el funcionamiento de cualquier programa consiste en objetos colaborando entre si mediante el envío de mensajes (“aproximadamente” métodos en Java o .NET). Cuales son esos objetos, cuales son esos mensajes forma parte de la tarea difícil del diseñador. Es ahí donde esta la fortaleza del diseño que hará que nuestro código sea fácil de mantener o una verdadera pesadilla.

Hernán destila Smalltalk por los poros y no tiene manera de disimularlo (y creo que tampoco quiere), personalmente valoro mucho esto porque nos permite aprender las técnicas en un entorno puro, ya habrá tiempo luego para “perder flexibilidad” en otras plataformas.

El próximo jueves o viernes la próxima entrega.

Nos vemos.

Principios SOLID 1, ejemplo con SRP, DIP y OCP

Les presento el primero de una serie de videos donde repetiré los refactorings basados en principios SOLID que mostré en la VAN de AltNetHispano. La ventaja que tienen estos videos sobre los de la VAN es que estan particionados, uno para cada ejemplo y vienen acompañados con el código fuente antes y después del ejercicio.

El repositorio de código fuente podrán encontralo en aquí y, una vez descargado, deberían ejecutar el siguiente comando para que quede tal como lo ven al principio del ejemplo:

$ git checkout ejemplos-base

Este comando posiciona el repositorio en el tag “ejemplos-base”, para dejarlo tal como queda al final de este ejemplo:

$ git checkout ejemplos-verificador

Mas abajo comento ciertos momentos interesantes en el video para aquellos que deseen un acceso directo.

En el minuto 1:20 presento el ejemplo del verificador.

En el minuto 3:45 explico el refactoring aplicando el principio de responsabilidad única (SRP).

En el minuto 6:35, una vez concluido el refactoring anterior, comienzo a analizar el código para identificar otro problema de diseño. Explico el principio de inversion de dependencias (DIP) y como aplicarlo al ejemplo.

En el minuto 11:10 doy un paso más con un problema de diseño mas sutil que termina con la descripción y aplicación al ejemplo del principio abierto-cerrado (OCP).

En el minuto 16:15 repaso los refactorings aplicados.

Si tuvieran alguna consulta o duda, no duden en contactarme.

Nos vemos.

De como Charly cantó sobre software

En su tema Dinosaurios Charly dice:

Los amigos del barrio pueden desaparecer
los cantores de radio pueden desaparecer
los que están en los diarios pueden desaparecer
la persona que amas puede desaparecer.

y sigue en la segunda estrofa:

Los que están en el aire pueden desaparecer, en el aire.
Los que están en la calle pueden desaparecer, en la calle.
Los amigos del barrio pueden desaparecer,
pero los dinosaurios van a desaparecer.

En el último renglón pronostica que los dinosaurios van a desaparecer como han aparecido y desaparecido innumerables dinosaurios en la industria del software.

Por supuesto que no entraré aquí en detalles sobre cuales son “mis” dinosaurios, seria una discusión inútil puesto que cada uno tendrá los suyos.

Lo mas importante viene a continuación:

Si los pesados, mi amor,
llevan todo ese montón
de equipaje en la mano
Oh! mi amor
yo quiero estar liviano.

y remata con el consejo:

Cuando el mundo tira para abajo
es mejor no estar atado a nada,
imaginen a los dinosaurios en la cama.

Aunque no hace falta que el mundo tire para abajo, la enseñanza para los que diseñamos software es bien directa: debemos andar livianos.

Andar livianos en en diseño de software es elegir con criterio y libertad los frameworks que necesitamos y, sobre todo, descartar lo que no necesitamos. Cuanto mas pequeños e independientes mejor.

No recomiendo esos frameworks que arrastran un montón de otros componentes, muchos menos los monolíticos que “solucionan todo”, desde la persistencia hasta la interfaz a usuario.

Andemos livianos colegas, no nos atemos a nada.

Sabiendo que soy mal comentarista, les dejo la letra y la versión cantada por Charly

Nos vemos.

Dependencias ocultas entre clientes y servicios

Hace unos días, en una revisión de código, encontré una definición de interfaz de un servicio que no me gustó. No fue inmediatamente evidente para mi pero uno de sus métodos era un code smell.

Esta es la interfaz:

<span style="color: blue;">public interface </span><span style="color: #2b91af;">IIpStsStore</span>{
    <span style="color: blue;">void </span>AddCredential(<span style="color: blue;">string </span>userName, <span style="color: blue;">string </span>password);
    <span style="color: blue;">void </span>RemoveCredential(<span style="color: blue;">string </span>userName);
    <span style="color: blue;">bool </span>CredentialExist(<span style="color: blue;">string </span>userName);
}

El método que me olía mal es el último. Luego de exprimir el cerebro un poco me di cuenta del motivo. Ese método, a mi juicio, propicia un acoplamiento innecesario entre el cliente del servicio y la implementación de dicho servicio.

Si exponemos ese método seguramente el cliente del servicio lo va a usar y no tendremos control de este uso, es decir, no sabremos exactamente cuando van a utilizarlo.

Supongamos que la primera implementación de ese servicio accede directamente al recurso donde se almacenan las credenciales. En ese caso, si el cliente llama a AddCredential() e inmediatamente llama a CredentialExist(), todo funcionará correctamente.

El problema aparecerá cuando hagamos otra implementación del servicio IpStsStore que funcione en forma asíncrona. En ese caso ya no habrá garantía de que la secuencia de métodos mencionada mas arriba siga funcionando.

En definitiva, tenemos un acoplamiento entre el cliente y la implementación del servicio, algo que siempre debe tratar de evitarse, además, como suele ocurrir en estos casos, luego de pensar un poco el diseño, ese método tampoco era necesario, un típico caso YAGNI.