language-agnostic - files - magic numbers gif
¿Qué es un número mágico y por qué es malo? (15)
Número mágico vs. Constante simbólica: ¿Cuándo reemplazar?
Magia: semántica desconocida.
Constante simbólica -> Proporciona tanto el contexto semántico correcto como el correcto para su uso
Semántica: el significado o propósito de una cosa.
"Cree una constante, asígnele el nombre y sustituya el número por ella". - Martin Fowler
Primero, los números mágicos no son solo números. Cualquier valor básico puede ser "mágico". Los valores básicos son entidades manifiestas como 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 precisos: 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, confuso o confuso. Esta es la noción de "magia". Un valor básico no es mágico cuando su significado semántico o el propósito de ser del ser es conocido, claro y comprendido (no confuso) de forma rápida y fácil, sin palabras de ayuda especial (por ejemplo, constante simbólica).
Por lo tanto, identificamos los números mágicos al medir la capacidad de un lector de código para saber, ser claros y comprender el significado y el propósito de un valor básico de su contexto circundante. Cuanto menos conocido, menos claro y más confundido sea el lector, más "mágico" es el valor básico.
Definiciones útiles
- confundir: hacer (a alguien) desconcertarse o perplejo.
- desconcertado: causa (a alguien) perplejo y confundido.
- perplejo: completamente desconcertado; muy desconcertado
- Desconcertado: totalmente desconcertante o perplejo.
- desconcertado: incapaz de entender; perplejo.
- comprender: percibir el significado deseado de (palabras, un idioma o un hablante).
- significado: lo que significa una palabra, texto, concepto o acción.
- significado: intención de transmitir, indicar o referirse a (una cosa o noción particular); significar.
- significar: ser una indicación de.
- Indicación: un signo o pieza de información que indica algo.
- indicar: señalar; espectáculo.
- signo: un objeto, calidad o evento cuya presencia o ocurrencia indica la probable presencia o ocurrencia de otra cosa.
Lo esencial
Tenemos dos escenarios para nuestros valores mágicos básicos. Solo el segundo es de importancia primordial para los programadores y el código:
- Un valor básico único (por ejemplo, un número) a partir del cual su significado es desconocido, incognoscible, incierto o confuso.
- Un valor básico (por ejemplo, un número) en contexto, pero su significado sigue siendo desconocido, incognoscible, confuso o confuso.
Una dependencia general de "magia" es la forma en que el único valor básico (por ejemplo, el número) no se conoce comúnmente como semántico (como Pi), sino que tiene una semántica conocida localmente (por ejemplo, su programa), que no está completamente claro en el contexto o podría ser objeto de abuso en contexto (s) bueno o malo.
La semántica de la mayoría de los lenguajes de programación no nos permitirá usar valores básicos solitarios, excepto (quizás) como datos (es decir, tablas de datos). Cuando encontramos "números mágicos", generalmente lo hacemos en un contexto. Por lo tanto, la respuesta a
"¿Reemplazo este número mágico con una constante simbólica?"
es:
"¿Qué tan rápido puedes evaluar y comprender el significado semántico del número (su propósito para estar allí) en su contexto?"
Tipo de magia, pero no del todo.
Con este pensamiento 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 (por ejemplo, 2 x 3.14159 x radio o 2 * Pi * r). Aquí, el número 3.14159 se reconoce mentalmente como Pi sin el identificador de la constante simbólica.
Aún 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 una necesidad de precisión) por lo general significa que el identificador simbólico o la 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 en el rancho
Dejando a un lado las constantes comunes como Pi, concentrémonos principalmente en números con significados especiales, pero esos significados están limitados al universo de nuestro sistema de software. Tal número podría ser "2" (como un valor entero básico).
Si uso el número 2 solo, mi primera pregunta podría ser: ¿Qué significa "2"? El significado de "2" en sí mismo es desconocido e incognoscible sin contexto, dejando su uso confuso y confuso. A pesar de que tener solo "2" en nuestro software no sucederá debido a la semántica del lenguaje, queremos ver que el "2" por sí solo no conlleva una semántica especial o el propósito obvio de estar solo.
Pongamos nuestro único "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 estimación de su semántica (significado y propósito). Podríamos detenernos aquí y decir que 2 está bien en este contexto y no hay nada más que necesitemos saber. Sin embargo, tal vez en nuestro universo de software esta no sea toda la historia. Hay más que eso, 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 el padding = 2
instrucciones padding = 2
no es suficiente. La noción de "por defecto" no se revela. Solo cuando escribo: padding = default_padding
como contexto y luego en otro lugar: default_padding = 2
me doy cuenta de un significado mejor y más completo (semántico y propósito) de 2 en nuestro sistema.
El ejemplo anterior es bastante bueno porque "2" por sí mismo podría ser cualquier cosa. Solo cuando limitamos el rango y el dominio de la comprensión a "mi programa", donde 2 es el default_padding
en las partes de GUI UX de "mi programa", finalmente 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 hacer que se use como default_padding
se comprende rápidamente en el contexto mayor del código adjunto. .
Por lo tanto, cualquier valor básico, cuyo significado (semántico y propósito) no se puede entender de manera suficiente y rápida es un buen candidato para una constante simbólica en lugar del valor básico (por ejemplo, el número mágico).
Ir más lejos
Los números en una escala también pueden tener semántica. Por ejemplo, imagina que estamos haciendo un juego D&D, donde tenemos la noción de un monstruo. Nuestro objeto monstruo tiene una característica llamada life_force
, que es un entero. Los números tienen significados que no son conocidos o claros sin palabras para proporcionar un significado. Así, comenzamos diciendo arbitrariamente:
- full_life_force: INTEGER = 10 - Muy vivo (y ileso)
- minimum_life_force: INTEGER = 1 - Apenas vivo (muy herido)
- muerto: INTEGER = 0 - Muerto
- muertos vivientes: INTEGER = -1 - Min muertos vivientes (casi muertos)
- zombie: INTEGER = -10 - Máximo no-muerto (muy no-muerto)
A partir de las constantes simbólicas de arriba, comenzamos a obtener una imagen mental de la vivacidad, muerte e "no-muerte" (y posibles ramificaciones o consecuencias) para nuestros monstruos en nuestro juego D&D. Sin estas palabras (constantes simbólicas), nos quedamos con los números que van desde -10 .. 10
. Solo el rango sin las palabras nos deja en un lugar de gran confusión posiblemente y potencialmente con errores en nuestro juego si diferentes partes del juego tienen dependencias en lo que significa ese rango de números para varias operaciones como attack_elves
o seek_magic_healing_potion
.
Por lo tanto, al buscar y considerar el reemplazo de "números mágicos", queremos hacer preguntas muy específicas sobre los números en el contexto de nuestro software e incluso cómo los números interactúan semánticamente entre sí.
Conclusión
Repasemos qué preguntas deberíamos hacer:
Es posible que tenga un número mágico si ...
- ¿Puede el valor básico tener un significado o propósito especial en su universo de software?
- ¿Es posible que el significado o propósito especial sea desconocido, incognoscible, incierto o confuso, incluso en su contexto adecuado?
- ¿Se puede usar incorrectamente un valor básico apropiado con malas consecuencias en el contexto incorrecto?
- ¿Se puede usar correctamente un valor básico inadecuado con malas consecuencias en el contexto correcto?
- ¿Tiene el valor básico una relación semántica o de propósito con otros valores básicos en contextos específicos?
- ¿Puede existir un valor básico en más de un lugar en nuestro código con diferentes semánticas en cada uno, lo que causa confusión en nuestro lector?
Examine valores básicos constantes independientes en su texto de código. Haga cada pregunta lenta y cuidadosamente sobre cada instancia de tal valor. Considera la fuerza de tu respuesta. Muchas veces, la respuesta no es en blanco y negro, 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 a la medida (en su mente) de la fortaleza o la debilidad del lector para establecer la conexión (por ejemplo, "obtener"). Cuanto más rápido comprendan el significado y el propósito, menos "magia" tienes.
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 surgen de las confusiones.
¿Qué es un número mágico?
¿Por qué debería evitarse?
¿Hay casos donde sea apropiado?
¿Has echado un vistazo a la entrada de Wikipedia para el número mágico?
Entra en un poco de detalle sobre todas las formas en que se hace la referencia del 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 usar 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 otros números en el código como constantes nombradas.
¿Qué hay de inicializar 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 (de acuerdo con 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.
¿Qué pasa con las variables de retorno?
Especialmente me resulta desafiante cuando implemento 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? Ok: establecerá existesCompany como false , pero también devolverá -1.
La opción antoher 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 la compañía existe.
Pero tengo miedo de la concurrencia, porque en este sistema, una empresa puede ser creada por otro usuario.
Por cierto, la conclusión es: ¿qué piensa acerca de 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?
@ eed3si9n: Incluso sugeriría que ''1'' es un número mágico. :-)
Un principio que está 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 proporcionó @marcio, puede terminar fácilmente duplicando ese hecho, y cuando comprenda que ese hecho cambia, tiene un problema de mantenimiento).
En la programación, un "número mágico" es un valor al que se le debe dar un nombre simbólico, pero en su lugar 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 (Single Point of Truth) es bueno: si quisiera cambiar esta constante más tarde, tendría que buscar en su código para encontrar todas las instancias. También es malo porque podría no estar claro para otros programadores qué representa este número, de ahí la "magia".
Las personas a veces llevan más lejos la eliminación de números mágicos, moviendo estas constantes a archivos separados para que actúen como configuración. A veces esto es útil, pero también puede crear más complejidad de lo que vale.
Otra ventaja de extraer un número mágico como una constante da la posibilidad de documentar claramente la información comercial.
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();
}
}
}
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 se puede verificar como una verificación de validez rápida. 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 formatos de archivo, ya que los archivos se pueden enviar de forma bastante promiscua y pierden los metadatos sobre cómo se crearon. Sin embargo, los números mágicos también se usan a veces para estructuras de datos en memoria, como las llamadas ioctl ().
Una comprobación rápida del número mágico antes de procesar el archivo o la estructura de datos permite que se señalen los errores antes, en lugar de obstaculizar todo el proceso potencialmente largo para anunciar que la entrada fue completa.
Supongo que esta es una respuesta a mi answer a tu pregunta anterior. En la programación, un número mágico es una constante numérica incrustada 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 estas dos razones, es importante aislar y definir las constantes numéricas fuera de los lugares donde se usan.
Un número mágico es el uso directo de un número en el código.
Por ejemplo, si tienes (en Java):
public class Foo {
public void setPassword(String password) {
// don''t do this
if (password.length() > 7) {
throw new InvalidArgumentException("password");
}
}
}
Esto debe ser refactorizado 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, siempre que cambie el tamaño máximo, tengo que cambiar en dos ubicaciones de código. Si me olvido de uno, esto llevará a inconsistencias.
El JDK está lleno de ejemplos como en clases de Integer
, Character
y Math
.
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.
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, supongamos que tiene una página que muestra los últimos 50 pedidos en una página de descripción general de "Sus pedidos". El 50 es el número mágico aquí, porque no está establecido a través del estándar o la convención, es un número que inventaste por las razones descritas en la especificación.
Ahora, lo que hace es tener 50 en diferentes lugares: su secuencia de comandos SQL ( SELECT TOP 50 * FROM orders
), su sitio web (sus últimos 50 pedidos), su inicio de sesión de pedido ( for (i = 0; i < 50; i++)
) y posiblemente muchos otros lugares.
Ahora, ¿qué pasa cuando alguien decide cambiar de 50 a 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 la función Buscar / Reemplazar no funcione, ya que 50 se puede usar para otras cosas, y el reemplazo a ciegas de 50 por 25 puede tener otros efectos secundarios negativos (es decir, su llamada Session.Timeout = 50
, que también está establecida en 25 y los usuarios también comienzan a informar) tiempos de espera frecuentes).
Además, el código puede ser difícil de entender, es decir, " if a < 50 then bla
"; si se encuentra con 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 en exactamente 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 necesita 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 de si eso está estandarizado). Además, todo lo que se define dentro de 1 función puede ser aceptable, pero eso depende del contexto.
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 una prueba de cordura.
Ejemplo: Abra cualquier archivo GIF, verá desde el 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 pueden contener estos mismos caracteres. Pero es muy poco probable.
En cuanto al intercambio de protocolos, puede usarlo para identificar rápidamente que el "mensaje" actual que se le está enviando está dañado o no es válido.
Los números mágicos siguen siendo útiles.
Un número mágico también puede ser un número con una semántica especial y codificada. Por ejemplo, una vez vi un sistema donde las 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 identificación.
Lo malo de esto es que estás reutilizando un espacio (el de los enteros con signo para los ID de registro) para habilidades especiales. Tal vez nunca querrá crear un registro con la ID 0, o con una ID negativa, pero incluso si no, todas las personas que miran el código o la base de datos pueden 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. ;-)
Un problema que no se ha mencionado con el uso de números mágicos ...
Si tiene muchos de ellos, las probabilidades son razonablemente buenas de tener dos propósitos diferentes para los que está usando números mágicos, en los que los valores son los mismos.
Y luego, por supuesto, necesita cambiar el valor ... con un solo propósito.
Vale la pena señalar que a veces sí desea números "no modificables" no configurables en su código. Hay varios famosos, incluido 0x5F3759DF, que se utiliza en el algoritmo de raíz cuadrada inversa optimizado.
En los raros casos en los que encuentro la necesidad de usar tales Números mágicos, los establezco como constantes en mi código y documenta por qué se usan, cómo funcionan y de dónde provienen.