¿Alguna vez es una buena idea codificar valores en nuestras aplicaciones? ¿O es siempre lo correcto llamar a este tipo de valores dinámicamente en caso de que necesiten cambiar?
programming-practices
Eduardo
fuente
fuente
pi
podría cambiar el valor de ...Respuestas:
Sí, pero hazlo obvio .
Hacer:
No:
fuente
diameter = 2 * radius
odiameter = RADIUS_TO_DIAMETER_FACTOR * radius
? De hecho, hay casos en los que un número mágico puede ser una mejor solución.diameter = radius << 1
? Supongo que eso también podría serdiameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
.diameter = radius.toDiameter()
Lo que me parece extraño de estas preguntas y respuestas hasta ahora es que nadie ha intentado definir claramente "código duro" o, lo que es más importante, las alternativas.
tl; dr: Sí, a veces es una buena idea codificar valores, pero no existe una regla simple sobre cuándo ; Depende completamente del contexto.
La pregunta lo reduce a valores , que considero que significan números mágicos , ¡pero la respuesta a si son una buena idea es relativa a lo que realmente se usan!
Varios ejemplos de valores "codificados" son:
Valores de configuración
Me estremezco cada vez que veo declaraciones como
command.Timeout = 600
. ¿Por qué 600? ¿Quién decidió eso? ¿Se estaba agotando el tiempo antes y alguien planteó el tiempo de espera como un truco en lugar de solucionar el problema de rendimiento subyacente? ¿O es realmente alguna expectativa conocida y documentada para el tiempo de procesamiento?Estos no deberían ser números mágicos o constantes, deberían estar externalizados en un archivo de configuración o base de datos en algún lugar con un nombre significativo, porque su valor óptimo está determinado en gran medida o completamente por el entorno en el que se ejecuta la aplicación.
Fórmulas matemáticas
Las fórmulas generalmente tienden a ser bastante estáticas, de modo que la naturaleza de los valores constantes en el interior no es realmente particularmente importante. El volumen de una pirámide es (1/3) b * h. ¿Nos importa de dónde vino el 1 o el 3? Realmente no. Un comentarista anterior señaló acertadamente que
diameter = radius * 2
probablemente sea mejor quediameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
eso, pero eso es una falsa dicotomía.Lo que debe hacer para este tipo de escenario es crear una función . No necesito saber cómo se te ocurrió la fórmula, pero aún necesito saber para qué sirve . Si, en lugar de cualquiera de las tonterías escritas anteriormente, escribo
volume = GetVolumeOfPyramid(base, height)
, de repente todo se vuelve mucho más claro, y está perfectamente bien tener números mágicos dentro de la función (return base * height / 3
) porque es obvio que son solo parte de la fórmula.La clave aquí es, por supuesto, tener funciones cortas y simples . Esto no funciona para funciones con 10 argumentos y 30 líneas de cálculos. Utilice la composición de funciones o constantes en ese caso.
Dominio / reglas comerciales
Esta siempre es el área gris porque depende de cuál sea exactamente el valor. La mayoría de las veces, estos números mágicos particulares son candidatos para convertirse en constantes, porque eso hace que el programa sea más fácil de entender sin complicar la lógica del programa. Considere la prueba
if Age < 19
vs.if Age < LegalDrinkingAge
; probablemente puedas descubrir qué está sucediendo sin la constante, pero es más fácil con el título descriptivo.Estos también pueden convertirse en candidatos para la abstracción de funciones, por ejemplo
function isLegalDrinkingAge(age) { return age >= 19 }
. Lo único es que a menudo su lógica de negocios es mucho más complicada que eso, y puede que no tenga sentido comenzar a escribir docenas de funciones con 20-30 parámetros cada una. Si no hay una abstracción clara basada en objetos y / o funciones, entonces está bien recurrir a constantes.La advertencia es que, si está trabajando para el departamento de impuestos, se vuelve realmente muy pesado y honestamente inútil escribir
AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
. No vas a hacer eso, lo harásAttachForm("B-46")
porque cada desarrollador que haya trabajado o vaya a trabajar allí sabrá que "B-46" es el código de formulario para un solo contribuyente que presenta bla, bla, bla. los códigos de forma son parte del dominio en sí, nunca cambian, por lo que no son realmente números mágicos.Por lo tanto, debe usar las constantes con moderación en la lógica empresarial; básicamente debes entender si ese "número mágico" es realmente un número mágico o si es un aspecto bien conocido del dominio. Si es dominio, entonces no lo codificas a menos que haya una muy buena posibilidad de que cambie.
Códigos de error y banderas de estado
Esto nunca está bien para el código duro, como
Previous action failed due to error code 46
puede decirle cualquier bastardo pobre que haya sido golpeado . Si su idioma lo admite, debería usar un tipo de enumeración. De lo contrario, generalmente tendrá un archivo / módulo completo lleno de constantes que especifican los valores válidos para un tipo de error en particular.¿Nunca me dejas ver
return 42
en un controlador de errores, capiche? No hay excusas.Probablemente omití varios escenarios, pero creo que eso cubre la mayoría de ellos.
Entonces, sí, a veces es una práctica aceptable codificar cosas. Simplemente no seas perezoso al respecto; debería ser una decisión consciente en lugar de un viejo código descuidado.
fuente
Hay varias razones para asignar un identificador a un número.
Esto nos da criterios para codificar literales. Deben ser inmutables, no difíciles de escribir, ocurrir en un solo lugar o contexto, y con un significado reconocible. No tiene sentido definir 0 como ARRAY_BEGINNING, por ejemplo, o 1 como ARRAY_INCREMENT.
fuente
Como una adición a otras respuestas. Use constantes para cadenas cuando sea posible. Por supuesto, no quieres tener
pero deberías tener
(suponiendo que realmente tenga una consulta en la que desea obtener todos los resultados de una tabla específica, siempre)
Aparte de eso, use constantes para cualquier número que no sea 0 (generalmente). Si necesita un permiso de máscara de bits de 255, no use
en su lugar usa
Por supuesto, junto con las constantes, sepa cuándo usar enumeradores. El caso anterior probablemente encajaría bien en uno.
fuente
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Depende de lo que consideres hardcoding. Si intenta evitar todas las cosas codificadas, termina en un territorio de codificación suave y crea un sistema que solo el creador puede administrar (y ese es el código rígido definitivo)
Muchas cosas están codificadas en cualquier marco razonable y funcionan. es decir, no hay una razón técnica por la cual no debería poder cambiar el punto de entrada de una aplicación C # (estático vacío principal), pero la codificación rígida no crea ningún problema para ningún usuario (excepto la pregunta ocasional de SO )
La regla general que uso es que cualquier cosa que pueda cambiar y que cambie, sin afectar el estado de todo el sistema, debe ser confusa.
Entonces, en mi humilde opinión, es una tontería no codificar cosas que nunca están cambiando (pi, constante gravitacional, una constante en una fórmula matemática - piense en el volumen de una esfera).
Además, es una tontería no codificar cosas o procesos que tendrán un impacto en su sistema que requerirá programación en cualquier caso, es decir, es un desperdicio permitir que el usuario agregue campos dinámicos a un formulario, si algún campo agregado requeriría que el desarrollador de mantenimiento entra y escribe un guión que haga que eso funcione. También es estúpido (y lo he visto varias veces en entornos empresariales) crear alguna herramienta de configuración, por lo que nada está codificado, sin embargo, solo los desarrolladores en el departamento de TI pueden usarlo, y es solo un poco más fácil de usar que para hacerlo en Visual Studio.
Entonces, en resumen, si una cosa debe estar codificada es una función de dos variables:
fuente
Codifico los valores solo si los valores se especifican en la Especificación (en una versión final de la especificación), por ejemplo, la respuesta HTTP OK siempre será
200
(a menos que cambie en el RFC), por lo que verá (en algunos de mis códigos ) constantes como:De lo contrario, almaceno constantes en el archivo de propiedades.
La razón por la que especifiqué las especificaciones es que el cambio de constantes en las especificaciones requiere una gestión de cambios, en la cual los interesados revisarán el cambio y lo aprobarán / desaprobarán. Nunca sucede de la noche a la mañana y toma meses / años para su aprobación. No olvide que muchos desarrolladores usan especificaciones (por ejemplo, HTTP), por lo que cambiarlo significa romper millones de sistemas.
fuente
fuente
Me di cuenta de que cada vez que puede extraer datos de su código, mejora lo que queda. Comienza a notar nuevas refactorizaciones y mejora secciones enteras de su código.
Es una buena idea trabajar para extraer constantes, no lo considere una regla estúpida, piense en ello como una oportunidad para codificar mejor.
La mayor ventaja sería la forma en que podría encontrar constantes similares como la única diferencia en los grupos de código: abstraerlos en matrices me ha ayudado a reducir algunos archivos en un 90% de su tamaño y, mientras tanto, corregir algunos errores de copiar y pegar .
Todavía tengo que ver una ventaja única para no extraer datos.
fuente
Recientemente codifiqué una función MySQL para calcular correctamente la distancia entre dos pares lat / long. No puedes simplemente hacer pitágorus; las líneas de longitud se acercan a medida que la latitud aumenta hacia los polos, por lo que hay un poco de trigonometría complicada. El punto es que estaba bastante dividido acerca de si codificar el valor que representa el radio de la Tierra en millas.
Terminé haciéndolo, aunque el hecho es que las líneas lat / lng están mucho más juntas, digamos, en la luna. Y mi función subestimaría drásticamente las distancias entre puntos en Júpiter. Me imaginé que las probabilidades de que el sitio web que estoy construyendo tenga una ubicación extraterrestre ingresada son bastante escasas.
fuente
Bueno, depende de si tu idioma está compilado. Si no está compilado, no es gran cosa, solo edita el código fuente, incluso si será un poco delicado para un no programador.
Si está programando con un lenguaje compilado, esto claramente no es una buena idea, porque si las variables cambian, tiene que volver a compilar, lo que es una gran pérdida de tiempo si desea ajustar esta variable.
No necesita hacer un control deslizante o una interfaz para cambiar dinámicamente su variable, pero lo menos que puede hacer es un archivo de texto.
Por ejemplo, con mi proyecto ogro, siempre estoy usando la clase ConfigFile para cargar una variable que he escrito en un archivo de configuración.
fuente
Dos ocasiones donde las constantes son (al menos en mi opinión) OK:
Constantes que se relacionan con nada más; puedes cambiar esas constantes cuando quieras sin tener que cambiar nada más. Ejemplo: el ancho predeterminado de una columna de cuadrícula.
Absolutamente inmutables, precisas, constantes obvias, como "número de días por semana".
days = weeks * 7
Reemplazar7
con una constanteDAYS_PER_WEEK
apenas proporciona ningún valor.fuente
Estoy completamente de acuerdo con Jonathan, pero como todas las reglas hay excepciones ...
"Número mágico en la especificación: Número mágico en el código"
Básicamente establece que cualquier número mágico que permanezca en la especificación después de intentos razonables de obtener un contexto descriptivo para ellos debe reflejarse como tal en el código. Si los números mágicos permanecen en el código, se deben hacer todos los esfuerzos para aislarlos y hacer que estén claramente vinculados a su punto de origen.
He realizado algunos contratos de interfaz donde es necesario completar mensajes con valores asignados desde la base de datos. En la mayoría de los casos, el mapeo es bastante sencillo y encajaría en las líneas de guía generales de Jonathan, pero he encontrado casos en los que la estructura del mensaje de destino era simplemente horrible. Más del 80% de los valores que debían transmitirse en la estructura eran constantes aplicadas por la especificación del sistema distante. Esto, junto con el hecho de que la estructura del mensaje era gigantesca, hizo que MUCHAS constantes tuvieran que ser pobladas. En la mayoría de los casos no proporcionaron un significado o razón, solo dijeron "pon M aquí" o "pon 4.10.53.10100.889450.4452 aquí". Tampoco intenté poner un comentario al lado de todos ellos, habría hecho que el código resultante fuera ilegible.
Dicho esto, cuando lo piensas ... se trata básicamente de hacerlo obvio ...
fuente
Si está codificando el valor de la constante gravitacional de la Tierra, a nadie le importará. Si codifica la dirección IP de su servidor proxy, tiene problemas.
fuente
Principalmente no, pero creo que vale la pena señalar que tendrá más problemas cuando comience a duplicar el valor codificado. Si no lo duplica (por ejemplo, úselo solo una vez en la implementación de una clase), entonces no usar una constante podría estar bien.
fuente