0

Clean Code #05 (2nd Edition): Variables y Números Mágicos

En post veremos como 2 code.smells importantes, la declaración de variables al principio de las clases y Números mágicos, comencemos.

Variables declaradas al principio

En los años 70 y 80, era muy común declarar las variables en la parte superior de los métodos. Esto se debía a que los compiladores de C  no entendían variables que estén declaradas en medio de los métodos. En estos días ya no existe estas limitaciones y no es necesario declararlas al principio.

Imaginemos que tenemos un gran cantidad de líneas de código, y declaramos todo al principio para todos los métodos. Esto nos crearía un esfuerzo extra. Deberíamos recordar todas las declaraciones mientras desarrollamos nuestro código. También, nos dificultará leer fácilmente nuevo código, para recordar que era, deberíamos volver al principio y ver donde están.

Veamos un código de ejemplo:

public class ProfityCalculator
    {
        private ProfitFrequency _profitFrequency;

        public ProfitCalculator(ProfitFrequency profitFrequency)
        {
            _profitFrequency = ProfitFrequency;
        }

        public decimal CalculateGross(decimal rate, decimal hours)
        {
            decimal overtimeHours = 0;
            decimal regularHours = 0;
            decimal regularProfit = 0;
            decimal overtimeProfit = 0;

            decimal grossProfit = 0;

            if (_profitFrequency == ProfitFrequency.Fortnightly)
            {
                if (hours > 80)
                {
                    overtimeHours = hours - 80;
                    regularHours = 80;
                }
                else
                    regularHours = hours;
            }


            else if (_profitFrequency == ProfitFrequency.Weekly)
            {
                if (hours > 40)
                {
                    overtimeHours = hours - 40;
                    regularHours = 40;
                }
                else
                    regularHours = hours;
            }


            if (overtimeHours > 0m)
            {
                overtimeProfit += (rate * 1.5m) * overtimeHours;
            }

            regularProfit = (regularHours * rate);
            grossProfit = regularProfit + overtimeProfit;

            return grossProfit;
        }

        public enum ProfitFrequency
        {
            Weekly,
            Fortnightly
        }

    }

Por ejemplo, en este código overtimeHours es usado en varios lugares, pero regularProfit. No es usado casi hasta el final. En este caso podemos usarlo como una declaración en línea para hacerlo más legible. 

Lo que podemos ver a simple vista, es que la asignación se ve de un color gris. Esto significa que la inicialización es innecesaria.

decimal regularHours = 0;
decimal regularProfit = 0;
decimal overtimeProfit = 0;

Seleccionamos y nos quedará de la siguiente manera: 

decimal regularProfit;

Pero, en realidad, lo mejor sería eliminar la variable y moverla debajo donde realmente se está utilizando:

var regularProfit = (regularHours * rate);
grossProfit = regularProfit + overtimeProfit;

Esto reduce el ruido generado al inicio del método. En el caso de overTimePay,  tiene una diferencia, este método toma un valor luego de una evaluación. Hay moverlo más cerca del codigo donde se usa.

decimal overtimeProfit = 0;

if (overtimeHours > 0m)
{
     overtimeProfit += (rate * 1.5m) * overtimeHours;
}

El caso de grossPay es el mismo que regularPay. Debemos seguir los mismos pasos. Para concluir, debemos declarar las variables lo más cerca posible de donde vamos a utilizarlas logrando facilitar la lectura del código.

Números mágicos

¿Que es un número mágico? Es cuando nuestro código, por ejemplo recibir un estado,no podemos determinar que es o que representa. Por esta razón, se lo llama número mágico. ¿Que es 1? ¿Que es 2? ¿Que significan?. Estos números pueden ser cualquier cosa que deseemos. Esto hace que sea difícil entender, leer y cambiar nuestro código. Por ejemplo:

public avoid ApproveCredit(int status)
{
    if(status == 1) // Approved
    {
        //...
    }else if(status == 2) // NoCredit
    { 
        //...
    }
}

Muchos desarrollos están en esta  situación. Los desarrolladores escriben comentarios para saber que es este número. Pero no es un buen camino, ensucia el código con comentarios que no son necesarios.

Podemos hacerlos más significativos declarando variables con un nombre que nos diga que es.

public avoid ApproveCredit(int status)
{
    const int approved = 1;
    if(status == approved) 
    {
        //...
    }else 
    { 
        const int NoCredit = 2;
        if(status == 2){
            //...
        }
    }
}

Esto estaría bien si solamente estos números tienen significado en el método. Pero imaginemos que estos numero pueden ser cross en toda nuestra aplicación. No es el camino más óptimo.

El mejor camino para solucionarlo es usar Enumeraciones. Volvamos nuestro código atrás y dejemoslo como estaba al principio. Creemos nuestra enumeración.

public enum CreditStatus{
    Approved = 1,
    NoCredit = 2
}

Ajustaremos nuestro código para que se adapte a la nueva característica que agregamos.

public avoid ApproveCredit(int status)
{    
    if(status == CreditStatus.Approved) 
    {
        //...
    }else if(status == CreditStatus.NoCredit){
            //...
    }
}

Si volvemos a nuestro código, nuestro código estará roto. La razón es que el tipo de datos que está recibiendo el método es del tipo Int y nuestras comparaciones, ahora, son por una enumeración.

Para repararlo podemos hacer un cast de la enumeración a int de la siguiente manera:

if(status == (int)CreditStatus.Approved)

Hay otro problema, debemos repetirlo en todos los lugares donde utilizamos valores de las enumeraciones. Mejor volvamos atrás.

El camino correcto es cambiar la firma del método por el tipo de enumeración que recibe. Para esto cambiamos el tipo del método de esta manera:

public avoid ApproveCredit(CreditStatus status)
{    
    if(status == CreditStatus.Approved) 
    {
        //...
    }else if(status == CreditStatus.NoCredit){
        //...
    }
}

Pero, podríamos tener otro problema. Todos los clientes del método se romperían. Sería un gran problema si fue consumido en muchos lugares deberíamos tocar código por toda nuestra aplicación.

Veamos otro ejemplo:

public void DisapprovedCredit(string status){

    switch(status){
        case "1":
            //...
            break;
        case "2":
            //...
            break;
    }
}

Si vemos el nombre del método está mal escrito y status ahora es un String. Esto sería un string mágico. Lo mismo que en paso anterior no sabemos que es 1 o que sera 2. Lo que haremos será cambiar la firma por la enumeración que creamos anteriormente y cambiar a mano cada uno de los case:

public void DisapprovedCredit(CreditStatus status){

    switch(status){
        case CreditStatus.Approved:
            //...
            break;
        case CreditStatus.NoCredit:
            //...
            break;
    }
}

Conclusiones

Recordemos que en los lenguajes modernos no es necesario declarar la variables al inicio del método. Si lo hacemos será dificultosa la lectura de nuestro código.

En situaciones donde encontramos números mágicos, el mejor camino es usar constantes si pertenecen al método o enumeraciones si se usan en varios lugares de nuestra aplicación.

En nuestro siguiente post veremos condicionales. Algo muy interesante y que debe ser usado correctamente.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *