[Clean Code] Parámetros boleanos (Flags) en funciones

El otro día cuando publiqué este post, alguien me comentó por Twitter que se podía usar un parámetro boleano (Flag) en la función… y comenté que escribiría otro post de por qué debemos evitar parámetros boleanos en nuestras funciones.

Imaginad que en el proyecto actual en el que estamos trabajando se requiere el uso de una caché de almacenamiento, una será persistente y otra temporal (disco o a memoria) y se ha implementado tal cual se muestra a continuación:

public class CacheSystem

{

    public void Add(object key, object value, bool toDisk)

    {

        if (toDisk)

        {

            ///To Disk

        }

        else

        {

            ///To Memory

        }

    }

}

¿Por qué esto está mal y debo evitarlo?

Primer punto a tener en cuenta y muy importante, “Una función sólo debería hacer una única cosa” y en nuestro caso está haciendo 2, escribir a disco o a memoria dependiendo de sí el llamante nos pasa true o false.

Segundo punto a tener en cuenta, mirad como quedaría una llamada a nuestra función en otra parte de la aplicación:

cacheSystem.Add("User", user, true);

Claramente se puede ver que estamos añadiendo algo a una caché pero, ¿Que nos está diciendo ese true? No queda muy claro ¿verdad? No hace nuestro código más legible sino todo lo contrario, dificulta su comprensión.

¿Como lo puedo solucionar?

En este caso hay 2 soluciones, empezaré por la más sencilla:

Partir la función en 2 funciones
public class CacheSystem

{

    public void AddToDisk(object key, object value)

    {

        

    }

 

    public void AddToMemory(object key, object value)

    {

 

    }

}

 

cacheSystem.AddToDisk("User", user);

cacheSystem.AddToMemory("User", user);

Creo que queda bastante claro ¿verdad? Diferenciamos la escritura a disco y a memoria. Ahora cada función hace sólo una cosa.

Crear implementaciones concretas

En este caso que hemos expuesto (No siempre se dará y entonces aplicaremos la primera solución) podemos crear una abstracción de la caché, bien con una clase abstracta o una interfaz:

public interface ICache

{

    void Add(object key, object value);

}

En base a esta interfaz, creamos 2 implementaciones:

public class DiskCache : ICache

{

    public void Add(object key, object value)

    {

        

    }

}

 

public class MemoryCache : ICache

{

    public void Add(object key, object value)

    {

        

    }

}

Y en nuestra aplicación, usar la que más nos convenga, por ejemplo, en las pruebas unitarias podemos usar siempre memoria que es más rápida. Esto se puede acompañar de un contenedor de dependencias y delegar en el la responsabilidad de la creación de dicha caché, que además podremos parametrizar vía código o fichero.

Espero que os haya gustado.

[Clean Code] Evita código duplicado

Seguro que te has encontrado más de una vez la casuística de tener 2 funciones con semejante código pero solo cambia una línea o un par de líneas de código, o un código común repetido por toda tu aplicación  ¿verdad? y por alguna razón u otra no te paras a refactorizar nunca, bien por tiempo o por desconocimiento. El problema es que si algo cambia en una aplicación ese es el código, así pues, tarde o temprano si tienes que cambiar ese código tendrás que hacerlo por todas las partes donde lo tengas, con el consiguiente problema de olvidar cambiarlo en alguna parte y por consiguiente tu aplicación tarde o temprano fallará (Sobre todo si no haces Test Unitarios).

A continuación os voy a mostrar como podemos refactorizar nuestro código cuando nos encontramos con este problema.

Suponemos que tenemos 2 métodos de una capa de aplicación que hacen lo mismo y solo cambia una línea:

Una entidad de dominio que llama a un método u otro.

Vamos a verlo.

public CommentDTO LikeComment(int commentId)

{

    ///Common code

    

    comment.Like(user);

 

    ///Common code

 

    return...

}

 

public CommentDTO UnLikeComment(int commentId)

{

    ///Common code

    

    comment.UnLike(user);

 

    ///Common code

 

    return...

}

Como podemos observar sólo cambia una línea de código y todo lo demás es igual, es decir, tenemos código duplicado y hay que evitarlo siempre!!!

¿Como podemos solucionarlo?

En este caso y usando las caracteristicas modernas del framework, podemos hacer uso de un delegado o una expression lambda (Si quieres aprender sobre estas, te recomiendo una serie del maestro Jose María AguilarC#: Desmitificando las expresiones lambda (I)), así pues, nuestro código una vez refactorizado quedaría así:

public CommentDTO LikeComment(int commentId)

{

    return CommentWrapper(commentId, (comment, user) => comment.Like(user));

}

 

public CommentDTO UnLikeComment(int commentId)

{

    return CommentWrapper(commentId, (comment, user) => comment.UnLike(user));

}

 

public CommentDTO CommentWrapper(int commentId, Action<Comment, User> user)

{

    ///Common code

 

    ///Specific code

    action.Invoke(comment, user);

 

    ///Common code

 

    return...

}

¿Mucho mejor no?

Ahora solo tenemos un único punto donde cambiar nuestro código común, además de poder reutilizarlo siempre que se traten de métodos relacionados con la entidad Comment y contenga esa misma firma, como en el caso anterior.

Espero comentarios, soluciones alternativas, críticas constructivas…

Un saludo

[Azure] Failed to delete Storage account ‘xxxxxxxxxxx’

Estoy empezando a jugar con Azure ahora que se avecinan días nubosos y me he encontrado con un error chorra que me ha llevado un tiempo darme cuenta.

Había creado unas máquinas virtuales balanceadas para hacer una serie de pruebas, y al finalizarlas, las he detenido, borrado y he intentado borrar el blob que tenía los vhd y me he encontrado este error:

image

El problema, es que no he borrado los discos de las máquinas virtuales. Para borrarlos:

  • Virtual Machines
  • Disks
  • Seleccionas el disco
  • Delete Disk

image

image

Con esto ya puede borrar tu blob de almacenamiento.

Un saludo