Pregunta

¿Qué es un número mágico?

¿Por qué debería evitarse?

¿Hay casos en los que es apropiado?

¿Fue útil?

Solución

Un número mágico es un uso directo de un número en el código.

Por ejemplo, si tiene (en Java):

public class Foo {
    public void setPassword(String password) {
         // don't do this
         if (password.length() > 7) {
              throw new InvalidArgumentException("password");
         }
    }
}

Esto debería refactorizarse para:

public class Foo {
    public static final int MAX_PASSWORD_SIZE = 7;

    public void setPassword(String password) {
         if (password.length() > MAX_PASSWORD_SIZE) {
              throw new InvalidArgumentException("password");
         }
    }
}

Mejora la legibilidad del código y es más fácil de mantener.Imagine el caso en el que configuro el tamaño del campo de contraseña en la GUI.Si uso un número mágico, cada vez que cambia el tamaño máximo, tengo que cambiar en dos ubicaciones del código.Si olvido uno, se producirán inconsistencias.

El JDK está lleno de ejemplos como en Integer, Character y Math clases.

PD:Las herramientas de análisis estático como FindBugs y PMD detectan el uso de números mágicos en su código y sugieren la refactorización.

Otros consejos

Un Número Mágico es un valor codificado que puede cambiar en una etapa posterior, pero que, por lo tanto, puede ser difícil de actualizar.

Por ejemplo, digamos que tiene una página que muestra los últimos 50 pedidos en una página de descripción general "Sus pedidos".50 es el número mágico aquí, porque no está establecido mediante un estándar o convención, es un número que usted inventó por las razones descritas en la especificación.

Ahora, lo que debe hacer es tener los 50 en diferentes lugares: su script SQL (SELECT TOP 50 * FROM orders), su sitio web (sus últimos 50 pedidos), el inicio de sesión de su pedido (for (i = 0; i < 50; i++)) y posiblemente en muchos otros lugares.

Ahora bien, ¿qué pasa cuando alguien decide cambiar 50 por 25?o 75?o 153?Ahora tienes que reemplazar los 50 en todos los lugares y es muy probable que te lo pierdas.Es posible que Buscar/Reemplazar no funcione, porque 50 puede usarse para otras cosas, y reemplazar ciegamente 50 por 25 puede tener otros efectos secundarios negativos (es decir,su Session.Timeout = 50 llamada, que también está configurada en 25 y los usuarios comienzan a informar tiempos de espera demasiado frecuentes).

Además, el código puede ser difícil de entender, es decir."if a < 50 then bla" - si te encuentras con eso en medio de una función complicada, otros desarrolladores que no estén familiarizados con el código pueden preguntarse "¿¿WTF es 50???"

Por eso es mejor tener números tan ambiguos y arbitrarios exactamente en 1 lugar: "const int NumOrdersToDisplay = 50", porque eso hace que el código sea más legible ("if a < NumOrdersToDisplay", también significa que solo necesitas cambiarlo en 1 lugar bien definido.

Los lugares donde los Números Mágicos son apropiados es todo lo que se define a través de un estándar, es decir. SmtpClient.DefaultPort = 25 o TCPPacketSize = whatever (No estoy seguro si eso está estandarizado).Además, todo lo que solo esté definido dentro de 1 función puede ser aceptable, pero eso depende del contexto.

¿Has echado un vistazo a la entrada de Wikipedia para ¿número mágico?

Se detalla un poco sobre todas las formas en que se hace referencia al número mágico.Aquí hay una cita sobre el número mágico como una mala práctica de programación.

El término número mágico también se refiere a la mala práctica de programación de utilizar números directamente en el código fuente sin explicación.En la mayoría de los casos, esto hace que los programas sean más difíciles de leer, comprender y mantener.Aunque la mayoría de las guías hacen una excepción para los números cero y uno, es una buena idea definir todos los demás números en el código como constantes con nombre.

Número mágico vs.Constante simbólica:¿Cuándo reemplazar?

Magia:Semántica desconocida

Constante simbólica -> Proporciona contexto semántico y correcto para su uso

Semántico:El significado o propósito de una cosa.

"Cree una constante, nombre después del significado y reemplace el número con él". - Martin Fowler

Primero, los números mágicos no son sólo números.Cualquier valor básico puede ser "mágico".Los valores básicos son entidades manifiestas como números enteros, reales, dobles, flotantes, fechas, cadenas, booleanos, caracteres, etc.El problema no es el tipo de datos, sino el aspecto "mágico" del valor tal como aparece en nuestro texto de código.

¿Qué entendemos por "magia"?Para ser preciso:Por "magia", pretendemos señalar la semántica (significado o propósito) del valor en el contexto de nuestro código;que es desconocido, incognoscible, poco claro o confuso.Ésta es la noción de "magia".Un valor básico no es mágico cuando su significado semántico o propósito de ser allí se conoce rápida y fácilmente, es claro y comprendido (no confuso) a partir del contexto circundante sin palabras auxiliares especiales (p. ej.constante simbólica).

Por lo tanto, identificamos números mágicos midiendo la capacidad de un lector de códigos para conocer, ser claro y comprender el significado y el propósito de un valor básico en el contexto que lo rodea.Cuanto menos conocido, menos claro y más confundido esté el lector, más "mágico" será el valor básico.

Definiciones útiles

  • confundir:causar que (alguien) se sienta desconcertado o perplejo.
  • desconcertado:causar que (alguien) se sienta perplejo y confundido.
  • perplejo:completamente desconcertado;muy desconcertado.
  • desconcertado:totalmente desconcertado o perplejo.
  • perplejo:incapaz de entender;perplejo.
  • entender:percibir el significado deseado de (palabras, un idioma o hablante).
  • significado:qué se entiende por palabra, texto, concepto o acción.
  • quiso decir:tiene la intención de transmitir, indicar o referirse a (una cosa o noción en particular);significar.
  • significar:ser una indicación de.
  • indicación:un signo o dato que indica algo.
  • indicar:señalar;espectáculo.
  • firmar:un objeto, cualidad o evento cuya presencia o ocurrencia indica la probable presencia o ocurrencia de algo más.

Lo esencial

Tenemos dos escenarios para nuestros valores básicos mágicos.Sólo el segundo es de primordial importancia para los programadores y el código:

  1. Un único valor básico (p. ej.número) cuyo significado es desconocido, incognoscible, poco claro o confuso.
  2. Un valor básico (p. ej.número) en contexto, pero su significado sigue siendo desconocido, incognoscible, poco claro o confuso.

Una dependencia general de la "magia" es cómo el único valor básico (p. ej.número) no tiene una semántica comúnmente conocida (como Pi), pero tiene una semántica conocida localmente (p. ej.su programa), que no está del todo claro según el contexto o se podría abusar de él en contextos buenos o malos.

La semántica de la mayoría de los lenguajes de programación no nos permitirá utilizar valores básicos solitarios, excepto (quizás) como datos (es decir,tablas de datos).Cuando nos encontramos con "números mágicos", generalmente lo hacemos en un contexto.Por lo tanto, la respuesta a

"¿Reemplazaré este número mágico con una constante simbólica?"

es:

"¿Qué tan rápido puede evaluar y comprender el significado semántico del número (su propósito de estar allí) en su contexto?"

Algo de magia, pero no del todo.

Con esta idea en mente, podemos ver rápidamente cómo un número como Pi (3,14159) no es un "número mágico" cuando se coloca en el contexto adecuado (p. ej.2 x 3,14159 x radio o 2*Pi*r).Aquí el número 3,14159 se reconoce mentalmente como Pi sin el identificador simbólico constante.

Aun así, generalmente reemplazamos 3,14159 con un identificador constante simbólico como Pi debido a la longitud y complejidad del número.Los aspectos de longitud y complejidad de Pi (junto con la necesidad de precisión) generalmente significan que el identificador simbólico o constante es menos propenso a errores.El reconocimiento de "Pi" como nombre es simplemente una ventaja conveniente, pero no es la razón principal para tener la constante.

Mientras tanto:De vuelta en el rancho

Dejando de lado constantes comunes como Pi, centrémonos principalmente en números con significados especiales, pero cuyos significados están restringidos al universo de nuestro sistema de software.Un número así podría ser "2" (como valor entero básico).

Si uso el número 2 solo, mi primera pregunta podría ser:¿Qué significa "2"?El significado de "2" por sí solo es desconocido e incognoscible sin contexto, lo que deja su uso poco claro y confuso.Aunque tener solo "2" en nuestro software no sucederá debido a la semántica del lenguaje, queremos ver que "2" por sí solo no conlleva una semántica especial ni un propósito obvio al estar solo.

Pongamos nuestro solitario "2" en un contexto de: padding := 2, donde el contexto es un "Contenedor GUI".En este contexto, el significado de 2 (como píxeles u otra unidad gráfica) nos ofrece una rápida idea de su semántica (significado y propósito).Podríamos detenernos aquí y decir que 2 está bien en este contexto y que no hay nada más que necesitemos saber.Sin embargo, quizás en nuestro universo del software esta no sea toda la historia.Hay más, pero "padding = 2" como contexto no puede revelarlo.

Supongamos además que 2 como relleno de píxeles en nuestro programa es de la variedad "default_padding" en todo nuestro sistema.Por lo tanto, escribir la instrucción padding = 2 No es suficientemente bueno.La noción de "incumplimiento" no se revela.Sólo cuando escribo: padding = default_padding como contexto y luego en otro lugar: default_padding = 2 ¿Me doy cuenta plenamente de un significado mejor y más completo (semántico y de propósito) de 2 en nuestro sistema?

El ejemplo anterior es bastante bueno porque "2" por sí solo podría ser cualquier cosa.Sólo cuando limitamos el alcance y el dominio de comprensión a "mi programa", donde 2 es el default_padding en las partes GUI UX de "mi programa", finalmente le damos sentido a "2" en su contexto adecuado.Aquí "2" es un número "mágico", que se factoriza en una constante simbólica. default_padding dentro del contexto de la GUI UX de "mi programa" para poder utilizarlo como default_padding entendido rápidamente en el contexto más amplio del código adjunto.

Por lo tanto, cualquier valor básico cuyo significado (semántico y propósito) no pueda entenderse suficiente y rápidamente es un buen candidato para una constante simbólica en lugar del valor básico (p. ej.número mágico).

Ir más lejos

Los números en una escala también pueden tener semántica.Por ejemplo, supongamos que estamos haciendo un juego de D&D, donde tenemos la noción de un monstruo.Nuestro objeto monstruo tiene una característica llamada life_force, que es un número entero.Los números tienen significados que no se pueden conocer ni son claros sin palabras que proporcionen significado.Así, comenzamos diciendo arbitrariamente:

  • fuerza_de_vida_completa:INTEGER = 10 -- Muy vivo (e ileso)
  • fuerza_vida_mínima:INTEGER = 1 -- Apenas vivo (muy herido)
  • muerto:ENTERO = 0 - Muerto
  • muertos vivientes:INTEGER = -1 -- Min muertos vivientes (casi muertos)
  • zombi:INTEGER = -10 - Máximo de muertos vivientes (muy muertos vivientes)

A partir de las constantes simbólicas anteriores, comenzamos a tener una imagen mental de la vida, la muerte y la "no-muerte" (y las posibles ramificaciones o consecuencias) de nuestros monstruos en nuestro juego de D&D.Sin estas palabras (constantes simbólicas), nos quedan sólo los números que van desde -10 .. 10.Solo el rango sin las palabras nos deja en un lugar de posiblemente gran confusión y potencialmente con errores en nuestro juego si diferentes partes del juego dependen de lo que significa ese rango de números para varias operaciones como attack_elves o seek_magic_healing_potion.

Por lo tanto, cuando buscamos y consideramos el reemplazo de "números mágicos", queremos hacer preguntas muy específicas sobre los números dentro del contexto de nuestro software e incluso sobre cómo los números interactúan semánticamente entre sí.

Conclusión

Repasemos qué preguntas debemos hacer:

Es posible que tengas un número mágico si...

  1. ¿Puede el valor básico tener un significado o propósito especial en el universo de su software?
  2. ¿Es probable que el significado o propósito especial sea desconocido, incognoscible, poco claro o confuso, incluso en su contexto adecuado?
  3. ¿Se puede utilizar incorrectamente un valor básico adecuado con malas consecuencias en el contexto equivocado?
  4. ¿Se puede utilizar correctamente un valor básico inadecuado con malas consecuencias en el contexto adecuado?
  5. ¿Tiene el valor básico relaciones semánticas o de propósito con otros valores básicos en contextos específicos?
  6. ¿Puede existir un valor básico en más de un lugar de nuestro código con una semántica diferente en cada uno, causando así confusión al lector?

Examine los valores básicos constantes del manifiesto independiente en el texto de su código.Haga cada pregunta lenta y cuidadosamente sobre cada caso de tal valor.Considere la fuerza de su respuesta.Muchas veces, la respuesta no es blanca o negra, sino que tiene matices de significado y propósito mal entendidos, velocidad de aprendizaje y velocidad de comprensión.También es necesario ver cómo se conecta a la máquina de software que lo rodea.

Al final, la respuesta al reemplazo es responder la medida (en su mente) de la fuerza o debilidad del lector para hacer la conexión (por ejemplo,"Consíguelo").Cuanto más rápido comprendan el significado y el propósito, menos "magia" tendrás.

CONCLUSIÓN:Reemplace los valores básicos con constantes simbólicas solo cuando la magia sea lo suficientemente grande como para causar errores difíciles de detectar que surjan de confusiones.

Un número mágico es una secuencia de caracteres al comienzo de un formato de archivo o intercambio de protocolo.Este número sirve como control de cordura.

Ejemplo:Abra cualquier archivo GIF, verá al principio:GIF89."GIF89" es el número mágico.

Otros programas pueden leer los primeros caracteres de un archivo e identificar correctamente los GIF.

El peligro es que los datos binarios aleatorios puedan contener estos mismos caracteres.Pero es muy improbable.

En cuanto al intercambio de protocolos, puede usarlo para identificar rápidamente que el "mensaje" actual que se le está pasando está dañado o no es válido.

Los números mágicos siguen siendo útiles.

En programación, un "número mágico" es un valor al que se le debe dar un nombre simbólico, pero que en cambio se introdujo en el código como un literal, generalmente en más de un lugar.

Es malo por la misma razón que SPOT (Punto Único de Verdad) es bueno:Si quisiera cambiar esta constante más adelante, tendría que buscar en su código para encontrar cada instancia.También es malo porque otros programadores pueden no tener claro qué representa este número, de ahí la "magia".

A veces, la gente lleva la eliminación de números mágicos más allá, moviendo estas constantes a archivos separados para que actúen como configuración.Esto a veces resulta útil, pero también puede crear más complejidad de la que merece la pena.

Un problema que no se ha mencionado con el uso de números mágicos...

Si tiene muchos de ellos, las probabilidades de que tenga dos diferentes son razonablemente buenas. propósitos para el que estás usando números mágicos, donde el valores resulta ser lo mismo.

Y luego, efectivamente, necesitas cambiar el valor...con un solo propósito.

Un número mágico también puede ser un número con una semántica especial codificada.Por ejemplo, una vez vi un sistema donde los ID de registro > 0 se trataban normalmente, 0 en sí era "nuevo registro", -1 era "esta es la raíz" y -99 era "esto se creó en la raíz".0 y -99 harían que el servicio web proporcione una nueva ID.

Lo malo de esto es que estás reutilizando un espacio (el de números enteros con signo para ID de registros) para habilidades especiales.Tal vez nunca quieras crear un registro con ID 0 o con un ID negativo, pero incluso si no, cada persona que mira el código o la base de datos puede tropezar con esto y confundirse al principio.No hace falta decir que esos valores especiales no estaban bien documentados.

Podría decirse que 22, 7, -12 y 620 también cuentan como números mágicos.;-)

Supongo que esto es una respuesta a mi respuesta a tu pregunta anterior.En programación, un número mágico es una constante numérica incorporada que aparece sin explicación.Si aparece en dos ubicaciones distintas, puede dar lugar a circunstancias en las que se cambie una instancia y no otra.Por ambas razones, es importante aislar y definir las constantes numéricas fuera de los lugares donde se utilizan.

Vale la pena señalar que a veces desea números "codificados" no configurables en su código.Hay una serie de los famosos incluido 0x5F3759DF que se utiliza en el algoritmo optimizado de raíz cuadrada inversa.

En los raros casos en los que encuentro la necesidad de usar dichos Números Mágicos, los configuro como constantes en mi código y documento por qué se usan, cómo funcionan y de dónde vienen.

Siempre he usado el término "número mágico" de manera diferente, como un valor oscuro almacenado dentro de una estructura de datos que puede verificarse como una verificación rápida de validez.Por ejemplo, los archivos gzip contienen 0x1f8b08 como sus primeros tres bytes, los archivos de clase Java comienzan con 0xcafebabe, etc.

A menudo se ven números mágicos incrustados en los formatos de archivos, porque los archivos pueden enviarse de manera bastante promiscua y perder cualquier metadato sobre cómo fueron creados.Sin embargo, los números mágicos también se utilizan a veces para estructuras de datos en memoria, como llamadas ioctl().

Una verificación rápida del número mágico antes de procesar el archivo o la estructura de datos permite señalar errores con anticipación, en lugar de esperar durante todo el procesamiento potencialmente largo para anunciar que la entrada fue una tontería completa.

¿Qué pasa con la inicialización de una variable en la parte superior de la clase con un valor predeterminado?Por ejemplo:

public class SomeClass {
    private int maxRows = 15000;
    ...
    // Inside another method
    for (int i = 0; i < maxRows; i++) {
        // Do something
    }

    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }

    public int getMaxRows() {
        return this.maxRows;
    }

En este caso, 15000 es un número mágico (según CheckStyles).Para mí, establecer un valor predeterminado está bien.No quiero tener que hacer:

private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;

¿Eso hace que sea más difícil de leer?Nunca consideré esto hasta que instalé CheckStyles.

@eed3si9n:Incluso sugeriría que el '1' es un número mágico.:-)

Un principio relacionado con los números mágicos es que cada hecho que trata su código debe declararse exactamente una vez.Si usa números mágicos en su código (como el ejemplo de longitud de contraseña que dio @marcio, puede terminar fácilmente duplicando ese hecho, y cuando su comprensión de ese hecho cambia, tendrá un problema de mantenimiento.

¿Qué pasa con las variables de retorno?

Me resulta especialmente desafiante a la hora de implementar procedimientos almacenados.

Imagine el siguiente procedimiento almacenado (sintaxis incorrecta, lo sé, solo para mostrar un ejemplo):

int procGetIdCompanyByName(string companyName);

Devuelve el ID de la empresa si existe en una tabla en particular.De lo contrario, devuelve -1.De alguna manera es un número mágico.Algunas de las recomendaciones que he leído hasta ahora dicen que realmente tendré que diseñar algo así:

int procGetIdCompanyByName(string companyName, bool existsCompany);

Por cierto, ¿qué debería devolver si la empresa no existe?De acuerdo:se establecerá existeempresa como FALSO, pero también devolverá -1.

Otra opción es hacer dos funciones separadas:

bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);

Entonces, una condición previa para el segundo procedimiento almacenado es que exista la empresa.

Pero tengo miedo de la concurrencia, porque en este sistema, otro usuario puede crear una empresa.

Por cierto, la conclusión es:¿Qué opinas sobre usar ese tipo de "números mágicos" que son relativamente conocidos y seguros para decir que algo no tiene éxito o que algo no existe?

Otra ventaja de extraer un número mágico como constante es la posibilidad de documentar claramente la información empresarial.

public class Foo {
    /** 
     * Max age in year to get child rate for airline tickets
     * 
     * The value of the constant is {@value}
     */
    public static final int MAX_AGE_FOR_CHILD_RATE = 2;

    public void computeRate() {
         if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
               applyChildRate();
         }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top