¿Qué es un número mágico?
¿Por qué debería ser evitado?
¿Hay casos donde es apropiado?
¿Qué es un número mágico?
¿Por qué debería ser evitado?
¿Hay casos donde es apropiado?
Respuestas:
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 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, cada vez que cambia el tamaño máximo, tengo que cambiar en dos ubicaciones de código. Si olvido uno, esto conducirá a inconsistencias.
El JDK está llena 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.
TRUE
/ FALSE
)
Un número mágico es un valor codificado que puede cambiar en una etapa posterior, pero que 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 resumen "Sus pedidos". 50 es el Número Mágico aquí, porque no se establece a través de estándares o convenciones, 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), su inicio de sesión de pedido ( for (i = 0; i < 50; i++)
) y posiblemente muchos otros lugares.
Ahora, ¿qué sucede cuando alguien decide cambiar de 50 a 25? o 75? o 153? Ahora tiene que reemplazar los 50 en todos los lugares, y es muy probable que se lo pierda. Buscar / Reemplazar puede no funcionar, porque 50 puede usarse para otras cosas, y reemplazar a ciegas 50 por 25 puede tener algunos 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 encuentra eso en medio de una función complicada, otros desarrolladores que no están familiarizados con el código pueden preguntarse "¿WTF tiene 50 ???"
Es por eso que 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 si eso está estandarizado). Además, todo lo que solo se define dentro de 1 función puede ser aceptable, pero eso depende del contexto.
SmtpClient.DefaultPort = 25
es posiblemente clara er a SmtpClient.DefaultPort = DEFAULT_SMTP_PORT
.
25
toda la aplicación y asegurarse de que solo cambie las ocurrencias del 25
puerto SMTP, no los 25 que son, por ejemplo, el ancho de una columna de tabla o el número de registros para mostrar en una página.
IANA
.
¿Has echado un vistazo a la entrada de Wikipedia para el número mágico?
Entra en detalles 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 demás números en el código como constantes con nombre.
Magia: semántica desconocida
Constante simbólica -> Proporciona el contexto semántico correcto y el correcto para su uso
Semántica: El significado o propósito de una cosa.
"Cree una constante, asígnele el nombre después del significado y reemplace el número con 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é queremos decir con "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 su propósito de ser se conoce rápida y fácilmente, es claro y se entiende (no es confuso) del contexto envolvente sin palabras auxiliares especiales (por ejemplo, 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 a partir del contexto que lo rodea. Cuanto menos conocido, menos claro y más confundido esté el lector, más "mágico" es el valor básico.
Tenemos dos escenarios para nuestros valores básicos mágicos. Solo el segundo es de importancia primordial para los programadores y el código:
Una dependencia general de la "magia" es cómo el único valor básico (por ejemplo, el número) no tiene una semántica conocida comúnmente (como Pi), pero tiene una semántica conocida localmente (por ejemplo, su programa), que no está del todo clara por el contexto o podría ser abusada en contextos buenos o malos.
La semántica de la mayoría de los lenguajes de programación no nos permitirá usar valores básicos solitarios, excepto (tal vez) 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:
"¿Con qué rapidez puede evaluar y comprender el significado semántico del número (su propósito de estar allí) en su contexto?"
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 Pi sin el identificador simbólico constante.
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) generalmente significa 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.
Dejando a un lado las 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. 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, 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 tiene una semántica especial o un propósito obvio de 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 suposición rápida 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 de 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 las instrucciones padding = 2
no es lo suficientemente bueno. La noción de "incumplimiento" 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" en sí mismo podría ser cualquier cosa. Solo cuando limitamos el alcance y el dominio de la comprensión a "mi programa", donde 2 es la parte default_padding
de GUI UX de "mi programa", finalmente entendemos "2" en su contexto adecuado. Aquí "2" es un número "mágico", que se descompone en una constante simbólica default_padding
dentro del contexto de la GUI UX de "mi programa" para hacer que se use tan default_padding
rápidamente como se entiende 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 se pueda comprender de manera suficiente y rápida es un buen candidato para una constante simbólica en lugar del valor básico (por ejemplo, número mágico).
Los números a escala también pueden tener semántica. Por ejemplo, imagina 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 son conocidos o claros sin palabras para proporcionar significado. Por lo tanto, comenzamos diciendo arbitrariamente:
A partir de las constantes simbólicas anteriores, comenzamos a obtener una imagen mental de la vida, la muerte y la "no muerte" (y posibles ramificaciones o consecuencias) para nuestros monstruos en nuestro juego D&D. Sin estas palabras (constantes simbólicas), solo nos quedan los números que van desde -10 .. 10
. Solo el rango sin las palabras nos deja en un lugar posiblemente de gran confusión y potencialmente con errores en nuestro juego si diferentes partes del juego dependen de lo que ese rango de números significa 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 completas sobre los números dentro del contexto de nuestro software e incluso cómo los números interactúan semánticamente entre sí.
Repasemos qué preguntas debemos hacer:
Es posible que tenga un número mágico si ...
Examine los valores básicos constantes manifiestos independientes en el texto de su 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 incomprendido, 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, "conseguirlo"). Cuanto más rápidamente entiendan el significado y el propósito, menos "mágica" tendrás.
CONCLUSIÓN: Reemplace los valores básicos con constantes simbólicas solo cuando la magia es lo suficientemente grande como para causar errores difíciles de detectar que surgen de las 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 un 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 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á transmitiendo está dañado o no es válido.
Los números mágicos siguen siendo útiles.
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 deslizó 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 desea cambiar esta constante más adelante, tendría que buscar su código para encontrar cada instancia. También es malo porque podría no estar claro para otros programadores qué representa este número, de ahí la "magia".
La gente a veces lleva la eliminación del número mágico más allá, al mover estas constantes en archivos separados para actuar como configuración. Esto a veces es útil, pero también puede crear más complejidad de lo que vale.
(foo[i]+foo[i+1]+foo[i+2]+1)/3
puede evaluarse mucho más rápido que un bucle. Si se reemplazara 3
sin reescribir el código como un bucle, alguien que lo ITEMS_TO_AVERAGE
definiera 3
podría pensar que podría cambiarlo 5
y que el código promediara más elementos. Por el contrario, alguien que mirara la expresión con el literal se 3
daría cuenta de que 3
representa el número de elementos que se suman.
Un número mágico también puede ser un número con semántica especial y codificada. Por ejemplo, una vez vi un sistema en el que las ID de registro> 0 se trataban normalmente, 0 en sí mismo era "nuevo registro", -1 era "esta es la raíz" y -99 era "esto se creó en la raíz". 0 y -99 causarían que WebService proporcione una nueva ID.
Lo malo de esto es que estás reutilizando un espacio (el de enteros firmados para ID de registro) para habilidades especiales. Tal vez nunca desee crear un registro con ID 0, o con una ID negativa, pero incluso si no, cada persona que mira el código o la base de datos podría 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 que tenga dos propósitos diferentes para los que está usando números mágicos, donde los valores son los mismos.
Y luego, efectivamente, necesita cambiar el valor ... para un solo propósito.
Supongo que esta es una respuesta a mi respuesta a su 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 cambia una instancia y no otra. Por ambas razones, es importante aislar y definir las constantes numéricas fuera de los lugares donde se usan.
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 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 ve números mágicos incrustados en formatos de archivo, porque los archivos se pueden enviar de manera bastante promiscua y perder 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 verificación rápida del número mágico antes de procesar el archivo o la estructura de datos le permite a uno señalar los errores temprano, en lugar de pasar por un proceso potencialmente largo para anunciar que la entrada fue completa.
Vale la pena señalar que a veces usted quiere números "codificados" 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 configuro como una constante en mi código y documento por qué se usan, cómo funcionan y de dónde provienen.
¿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.
static final
constantes son excesivas cuando las usas en un método. Una final
variable declarada en la parte superior del método es más legible en mi humilde opinión.
@ eed3si9n: Incluso sugeriría que '1' es un número mágico. :-)
Un principio relacionado con los números mágicos es que cada hecho con el 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 comprenda ese hecho cambia, tiene un problema de mantenimiento.
factorial n = if n == BASE_CASE then BASE_VALUE else n * factorial (n - RECURSION_INPUT_CHANGE); RECURSION_INPUT_CHANGE = 1; BASE_CASE = 0; BASE_VALUE = 1
¿Qué pasa con las variables de retorno?
Especialmente me resulta difícil al 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? Ok: establecerá existesCompany como falso , pero también devolverá -1.
Una opción más es hacer dos funciones separadas:
bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);
Por lo tanto, 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.
La conclusión es: ¿qué piensa sobre el uso de 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 una constante da la posibilidad de documentar claramente la información del negocio.
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();
}
}
}
const myNum = 22; const number = myNum / 11;
ahora mis 11 podrían ser personas o botellas de cerveza o algo así, en cambio, cambiaría el 11 a una constante como los habitantes