2009-03-04 14 views
15

Al escribir código, especialmente cuando se trata de fechas y horas, debe trabajar con muchos números específicos, por ejemplo: 60 segundos en un minuto, 3600 = segundos en una hora.Números mágicos contra constantes con nombre

Algunas personas se limitan a utilizar los valores en bruto para muchos de estos, mientras que otros los ponen en constantes para aumentar la legibilidad.

por ejemplo:

$x = time() + 3600; 
$y = time() + 86400; 
$z = time() + 604800; 

// vs 

define('MINUTE', 60); 
define('HOUR', 60 * MINUTE); // 3600 
define('DAY', 24 * HOUR);  // 86400 
define('WEEK', 7 * DAY);  // 604800 

$x = time() + HOUR; 
$y = time() + DAY; 
$z = time() + WEEK; 

Por supuesto, el segundo es más fácil de leer, pero un poco exagerado para algunos de los valores más bajos, por lo dónde exactamente se traza la línea? Personalmente, no veo ningún problema con la legibilidad de 86400 (en mi cabeza, automáticamente lo leí como "24 horas"), pero dibujaría la línea en la constante SEMANA.

+0

C++ 14 '' más una buena biblioteca de fecha/hora le da un nuevo giro a esto: https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes # microfortnights :-) –

Respuesta

40

86400 no es aceptable, ya que se puede escribir mal fácilmente como 84600, 88400, etc.

Un escrito mal constante será un error de compilación

+0

Me encanta ver cómo la lógica y el razonamiento entran en lo que era una pregunta tan subjetiva. ¡Esfuerzo superior! – nickf

+0

También puedo escribir mal 60 (todavía no soy tan bueno en dos filas de la fila de inicio como debería). 50 se parece tanto a la cantidad de segundos en un minuto, para mí, como 84600, como el número de segundos en un día. –

+1

¡Sí, tienes razón! Solo observe cuántos errores cometieron las personas en https://github.com/search?q=84600+day&type=Code&ref=searchresults –

-1

Yo diría que HORA, MINUTO y DÍA están bien. Ir más granular no parece proporcionar mucha más legibilidad.

1

Dibujaría la línea según el tamaño del proyecto. Cuanto más grande es el proyecto, más abstracciones y constantes ... Tan simple como eso.

16

Uno de mis profesores nos dijo una vez que no pusiéramos números mágicos en nuestro código excepto 1, -1 y 0. Eso es un poco extremo, pero se me queda grabado y todavía me guía aunque no lo adhiera a eso completamente

En su ejemplo, preferiría los nombres simbólicos en todos los casos.

+1

No, eso no es lo suficientemente extremo. Todos los números deben definirse como constantes. De hecho, incluso las definiciones no deberían usar constantes, usaría "#define ONE one" si fuera posible, para que haya ** no ** números desnudos en el código :-) – paxdiablo

+0

y "#define one 1", ¿también? –

+1

He escuchado una historia de pesadilla de outsourcing que se relaciona. La palabra recayó sobre ellos para eliminar todos los números mágicos en el código. El equipo subcontratado creó un gran archivo de encabezado que no contenía nada más que "#define INT_1 1 #define INT_2 2", hasta 1000. –

4

Mi enfoque es que no se utiliza el nombre constantes, pero mantener las unidades separadas de esta manera:

long twelvePM = 12 * 60 * 60 * 1000L; 
long timeout = 60 * 1000L; 

De esta manera queda claro que esto son en milisegundos y son fáciles de ajustar en caso de que desee modificar los valores luego.

+1

long twelvePM = 12 * HOURS_IN_MS; tiempo de espera largo = 1 * MINUTES_IN_MS; – FryGuy

+0

Así es como yo también lo prefiero. Es bastante obvio lo que significa para el tiempo y uno debe notar que el tiempo representa el 99% de esta multiplicación de todos modos. (Sí, me doy cuenta de que esto no es muy empírico ;-)) – Caffeine

8

Iré constantes (o algún derivado cursi, como la convención 15.minutes de Rails) prácticamente en todas partes. Para mí, se trata de simplificar el "tipeo" de todo; si veo "10 * MINUTOS" en algún lugar de una línea, sé que estoy lidiando con el tiempo (o alguien está listo para una patada en el culo). Si veo 10 * 60 o 600 es muy posible que no me dé cuenta de que estamos lidiando con el tiempo con tanta facilidad.

+0

Eso es lo mismo que la notación húngara: está solucionando un problema que no podría estar allí en primer lugar. El código debe ser autoexplicativo y los nombres deben ser significativos, lo que prácticamente elimina la necesidad de largos comentarios y constantes. La única razón válida para usar constantes para mí es "D.R.Y.". – Caffeine

+0

@Caffeine, ¿Qué es la notación húngara en la publicación de @ womble? – strager

+0

Es una convención de codificación para nombrar variables muy utilizadas por Microsoft (http://en.wikipedia.org/wiki/Hungarian_notation). Me parece que si no puedes descubrir qué tipo tiene una variable, algo ha ido terriblemente mal. – Caffeine

2

Tiendo a usar constantes sobre números mágicos casi exclusivamente. Creo que aumenta de forma legible y le da un punto en el programa para corregir cualquier error. Hay varios '60' mágicos, por ejemplo: 60 segundos en un minuto, 60 minutos en una hora.

0

Un día siempre tendrá 24 horas, y una hora siempre será de 60 minutos.

Creo que el punto a nombrarlos como constantes descriptiva es donde a) el valor numérico no está clara (604.800?) b) el valor puede cambiar algún día

7

No es un relativamente buen argumento entonces cualquier un número distinto de cero o uno debe ser una constante con nombre. Incluso en su ejemplo en el que afirma que no tiene problemas con la legibilidad de 86400, todavía existe cierta ambigüedad acerca de cuál es su unidad de medida.

Si yo fuera el mantenimiento de su código, preferiría ver constantes con nombre como:

const int secondsInDay = 86400; 

..no hay mucha ambigüedad allí. :) Depende de si alguien (incluyéndote a ti mismo ... quiero decir, lucho para recordar lo que escribí la semana pasada, ¡menos aún el año pasado!) Será requerido para mantener tu código en algún momento.

+0

Sin ambigüedad, solo un gran * error *. (sonrisa) Pero demuestra bastante bien el poder de las constantes: si estuvieras usando números mágicos, nadie sabría si quisieras obtener los segundos en una hora o segundos en un día ... error grave de encontrar. – womble

+0

No puede permitirse asumir que nadie más mantendrá su código. Ese alguien podría ser usted, meses después, habiendo olvidado en su mayoría cómo funciona su código. :) – Parappa

+0

jaja, womble, estás muerto y punto excelente .. (error ahora corregido). –

4

digo a mis estudiantes: este

Si usted puede leer el código sin comentarios entonces no hay necesidad de constantes. Si tiene que tener explique el código, entonces necesita comentarios.

También les digo:

El pensamiento es malo. En eso, si escribes el código que hace que la gente tenga que cavar para entenderlo, es no es algo bueno.

Por lo tanto, si puede mostrar las líneas a un compañero de trabajo y pueden entenderlo sin constantes, usted es (probablemente) bueno sin ellas. Probablemente querrás las constantes.

2

Me mantengo simple; el nombre de la variable y el comentario (si es necesario en caso de números muy mágicos) serán suficientes para pasar la revisión del código.

int someDelay = 1232323; // in milliseconds. 
0

Personalmente, me gusta tener las constantes con nombre en caso de tener que cambiar el valor de la constante, entonces sólo tengo que hacerlo en un solo lugar.

+0

sí. hoursPerDay = 25; // ¡muhahahaha! – bendin

0

sin duda lo utilizo constantes, mientras que yo U puede mira 86400 y como 24 horas, un futuro programador que hace el mantenimiento puede no ser tan brillante y se rascará la cabeza en lo que significa exactamente ese número. Este también es el caso para usted si regresa un día y olvida lo que significa 86400.

0
$x = time() + HOUR; 
$y = time() + DAY; 
$z = time() + WEEK; 

¿Qué hay de cuerdas mágicas? Que tienden a hacer cosas como esta:

$x = strtotime('now + 1 hour'); 
    $y = strtotime('now + 1 day'); 
    $z = strtotime('now + 1 week'); 

Sí, es probable que se ejecuta fraccionadamente más lento. Pero en el mayor esquema de cosas, ¿realmente importa?

+1

Es posible que el rendimiento no sea relevante. Pero el compilador no puede verificar cadenas y eso es malo si hay disponible una alternativa con verificación de tiempo de compilación, como se muestra en los otros ejemplos. – Bananeweizen

4

Personalmente, usaría SECS_PER_MIN, SECS_PER_HOUR, etc. Incluso he conocido NANOS_PER_SEC en ocasiones. Siempre lo haría si un idioma careciera de notación científica para literales enteros.

No se trata de legibilidad, exactamente. El motivo de usar SECS_PER_DAY en lugar de 86400 no tiene que ver solo con el conocimiento general que esperamos del lector. Se trata de código de auto-documentación, lo que significa que muestra inequívocamente su trabajo.

Claro, todo el mundo sabe que hay 60 segundos en un minuto. Pero también saben que hay 60 minutos en una hora. Si usa un literal de 60, probablemente sea obvio lo que su código tiene la intención de hacer. Pero, ¿por qué arriesgarse?

Si usa SECS_PER_MIN, entonces definitivamente es obvio que está convirtiendo un tiempo en minutos (solo 1 minuto, en su ejemplo) a un tiempo en segundos. Por ejemplo, no está agregando de una hora a un tiempo medido en minutos, o de un grado a un ángulo en minutos. No está agregando 5 pies a una longitud medida en pulgadas.

Las constantes con nombre proporcionan más contexto para el código circundante. Para su ejemplo de agregar un tiempo, sabemos simplemente mirando una línea que $ x necesita ser un tiempo en segundos. El nombre de la constante reitera que $ x no es un tiempo en milisegundos, o marcas de reloj, o microfortnights. Esto hace que sea más fácil para todos verificar y mantener la corrección de las unidades: pueden ver al mirar que lo que intentaron hacer es lo que realmente hicieron. Nunca tienen que siquiera considerar la idea de que quisiste agregar 60 milisegundos a un tiempo medido en milisegundos, pero te equivocaste porque $ x en realidad estaba en segundos.

Las constantes con nombre también ayudan a evitar errores tipográficos. Claro, todos saben que hay 86400 segundos en un día. No se desprende necesariamente que no escriban esto como 84600, o que inmediatamente detectarán el error si alguien más lo ha hecho. De acuerdo, tienes pruebas completas, por lo que un error así nunca llegaría al campo, pero la forma más eficiente de solucionarlo es evitar que el código defectuoso lo haga probar en primer lugar. Así que definiría las constantes de este tipo (o cualquier sintaxis):

SECS_PER_MIN := 60; 
SECS_PER_HOUR := 60 * SECS_PER_MIN; 
SECS_PER_DAY := 24 * SECS_PER_HOUR; 
SECS_PER_WEEK := 7 * SECS_PER_DAY; 

O, si se necesitaban las otras constantes de todos modos (que en el caso del tiempo que probablemente no porque normalizar todo en segundos en la primera oportunidad de reducir las posibilidades de confusión):

SECS_PER_MIN := 60; 
MINS_PER_HOUR := 60; 
HOURS_PER_DAY := 24; 
DAYS_PER_WEEK := 7; 

SECS_PER_HOUR := SECS_PER_MIN * MINS_PER_HOUR; 
etc. 

Nota del orden en el lado derecho: los minutos visiblemente "cancelar", haciendo que el trabajo aún más clara. No es tan importante con el tiempo, pero es bueno establecer un esquema coherente desde el principio, de modo que cuando las cosas se pongan feas más tarde (CLOCKS_PER_SEC, PLANCK_LENGTHS_PER_PARSEC) pueda hacerlo bien utilizando técnicas familiares.

+0

También hay 60 segundos en un minuto, y 60 minutos en un grado, y 360 grados en un círculo. Términos tales como "hora" o "grado" mantendrán las cosas en orden, incluso en una aplicación de geometría o geografía. –

3

Para su uso en unidades de este tipo, un buen truco es nombrar todos las unidades (incluso el de bases), de esa manera su código se lee como una medida adecuada especificada:

// The base unit of time is the second 
const double second = 1.0; 
const double ns = 1e-9 * second; 
const double micros = 1e-6 * second; 
const double ms = 1e-3 * second; 
const double minute = 60.0 * second; 
const double hour = 60 * minute; 
const double day = 24 * hour; 
const double week = 7 * day; 
const double year = 365.24 * day; 

continuación siempre utilizar la unidad apropiada en el código

// Set up a 90 second timeout 
timeout(90*second); 

o

elapsedDays = floor(elapsedtime/day); 

Puede ver esta formulación en varios paquetes científicos (por ejemplo, Geant4) de vez en cuando.

+0

¡Alto! ¿Por qué está utilizando el doble para representar algo tan importante como el tiempo? Use un tipo entero como long, o bignum si eso no es lo suficientemente grande. Se encontrará con problemas de coma flotante – Pyrolistical

+0

porque estas son * medidas * y no tienen una precisión indefinidamente alta en ningún caso. Con un doble, puedes perder alrededor de 10 dígitos decimales de precisión y seguir siendo consistente con una muy buena medición. Lo mismo para distancias, energías, etc. Vea Geant4, y pregunte de nuevo. – dmckee

+0

Y corren rápidamente, lo cual no ocurre con los bignums y tienen un rango dinámico enorme que el tipo de entero de longitud fija no lo hace. – dmckee

0

Puedo estar pidiendo votos hacia abajo aquí, pero creo que ninguno de sus ejemplos son números mágicos. Son constantes universales bien definidas que nunca tendrás que sustituir con otra cosa.

Mi enfoque es una combinación de algunas de las respuestas anteriores:

// properly named variables 
int daysBeforeFriday = 4; 

// expanded math expressions 
int minutesBeforeFriday = 4 * 24 * 60; 

// 4 days in seconds (clarification comments) 
int secondsBeforeFriday = 4 * 24 * 60 * 60; 

// etc.. 

Quisiera, sin embargo, calcular previamente determinadas expresiones dentro de una constante en los casos en que los valores implicados no son obvias o la optimización y/o capacidad de repetición son preocupaciones (valores de coma flotante y sus errores de redondeo, por ejemplo).

Perdónenme por usar C como un ejemplo de este último caso, pero busque here para entender lo que quiero decir. ;)

0

utilizo constantes con nombre

  • Si el número va a aparecer en varios lugares
  • Si el número podría cambiar nunca (nueva configuración, el nuevo proyecto)

veces me incrusta el número, como una pista de legibilidad.

#define LPP57 57  // Lines per page 

etc.

+0

Sugiero también donde el significado puede ser confuso. Si el número 47 aparece solo una vez en su código, y nunca cambiará, aún así preferiría ver por qué es 47. –

+0

Creo que preferiría ver 57 que LPP57. Ambos apestan, así que solo elige el más fácil de 2. ¿Por qué es tan difícil escribir toda la frase para que pueda leerse? – Dunk

0

Es importante la legibilidad que sus constantes incluyen las unidades de medida. De lo contrario

$ x = tiempo() + HORA;

es apenas mejor. ¿Cómo sabemos que $ x, la función de tiempo() y la HORA constante son todos números en unidades de segundos? Bueno, no podemos cambiar el nombre de la función time(), pero al menos podemos escribir

$ x_seconds = time() + SECONDS_PER_HOUR;

y ahora está claro, sin necesidad de comentar.

De hecho, ¿cómo sabemos que no está haciendo una concatenación de cadenas, es decir, agregando el sufijo "am" o "pm". Podrías decir "bueno, nadie en su sano juicio llamaría a esa HORA", pero no estoy tan seguro.

Algunas personas abogan por la notación húngara para este propósito. No me gusta la forma de húngaro que decora nombres con su tipo subyacente (int, handle, etc.) pero me gusta la decoración con el tipo semántico (segundos).

-1

Trabaja con el más alto nivel de abstracción disponible.

Rubí ActiveSupport Ejemplo

3.months.ago 
1.year.from_now 

El mismo principio se puede aplicar a otros idiomas menos flexibles, así, Java le permitirá hacer algo como:

TimeHelper.months().ago(3); 

Si usted no tiene objetos en su idioma que puede hacer esto:

$x = strtotime('now + 1 hour'); 
$y = strtotime('now + 1 day'); 
$z = strtotime('now + 1 week'); 

Mucho menos espacio para la malinterpretación tación. ¿Por qué jugar con cosas que están por debajo del dominio del problema si no es necesario?

+0

Bueno, no todos los idiomas tienen esas sutilezas, por lo que hay una razón para usar uno u otro. – nickf

+0

Eso no significa que no pueda aplicar los mismos principios, incluso en Java puede hacer algo como TimeHelper.months(). Ago (3) y hacer que devuelva lo que tenga sentido agregar a su fecha/hora. –

+0

"¿Por qué perder el tiempo jugando con cosas tan por debajo del dominio del problema si no es necesario?" No consideraría agregar segundos fuera o debajo del dominio problemático de trabajar con fechas ... – nickf

0

Depende un poco del idioma, pero crearía un tipo. De esta forma, podrá leer la intención del código y que el sistema de verificación de tipo verifique algo de esa intención en tiempo de compilación y/o tiempo de ejecución.

Las constantes están ocultas dentro de los métodos con nombre relevante, y no se repiten (puede probar el tipo/constantes si lo desea).

Por supuesto, puede haber algunos problemas de optimización/rendimiento con este enfoque en algunos casos.Pero en mi experiencia, rara vez han importado.

p. Ej. para la ilustración algo como:

class Time 
{ 
    private long time; 

    Time(long hours, long minutes, long seconds, long milliseconds) { ... } 
    Time(long milliseconds) { ... } 

    Time addMilliseconds(long increment) 
    { 
     return new Time(time + increment)); 
    } 

    Time addSeconds(long increment) 
    { 
     return addMilliseconds(increment * 1000); 
    } 

    Time addMinutes(long increment) 
    { 
     addSeconds(increment * 60); 
    }  

    Time addHours(long increment) 
    { 
     addMinutes(increment * 60); 
    } 

    [...] 

    long getTimeInMilliseconds() { ... }; 
    long getTimeInSeconds() { ... }; 
    long getTimeInMinutes() { ... }; 

    [.. maybe some static factory methods ...] 
} 

que se puede utilizar de esta manera:

Time oneHourAhead = new Time(now()).addHours(1); 
Time tenMinutesAgo = new Time(now()).addMinutes(-10); 
Time oneHourTenMinutesAhead = new Time(now()).addHours(1).addMinutes(10); 
2

Mi regla de oro en la enseñanza (así, reglas más duros de la base que en la vida real) era cualquier número distinto - 1, 0, 1 o 2 que se usaron más de una vez NECESITARON ser una constante. SI lo usa solo una vez, puede comentarlo si lo prefiere ...

Cuestiones relacionadas