Uso de cadenas / números mágicos [cerrado]

31

Este es un tema un tanto controvertido, y supongo que hay tantas opiniones como programadores. Pero en aras de eso, quiero saber cuáles son las prácticas comunes en los negocios (o en sus lugares de trabajo).

En mi lugar de trabajo tenemos unas estrictas pautas de codificación. Una sección de eso está dedicada a cadenas / números mágicos. Establece (para C #):

No use valores literales, ya sean numéricos o de cadenas, en su código que no sea para definir constantes simbólicas. Use el siguiente patrón para definir constantes:

public class Whatever  
{  
   public static readonly Color PapayaWhip = new Color(0xFFEFD5);  
   public const int MaxNumberOfWheels = 18;  
}

Hay excepciones: los valores 0, 1 y nulo casi siempre se pueden usar de forma segura. Muy a menudo, los valores 2 y -1 también están bien. Las cadenas destinadas al registro o al rastreo están exentas de esta regla. Los literales están permitidos cuando su significado es claro por el contexto, y no están sujetos a cambios futuros.

mean = (a + b) / 2; // okay  
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough

Una situación ideal sería algún documento de investigación oficial que muestre los efectos sobre la legibilidad / mantenibilidad del código cuando:

  • Los números / cuerdas mágicas están por todas partes
  • Las cadenas / números mágicos se reemplazan por declaraciones constantes razonablemente (o en diferentes grados de cobertura), y por favor no me griten por usar "razonablemente", sé que todos tienen una idea diferente de lo que es "razonablemente"
  • Las cadenas / números mágicos se reemplazan en exceso y en lugares donde no tendrían que estar (vea mi ejemplo a continuación)

Me gustaría hacer esto para tener algunos argumentos con base científica cuando discuta con uno de mis colegas, quien va al punto de declarar constantes como:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Otro ejemplo sería (y este está en JavaScript):

var someNumericDisplay = new NumericDisplay("#Div_ID_Here");

¿Pega ID de DOM encima de su archivo javascript si esa ID se usa solo en 1 lugar?

He leído los siguientes temas:
StackExchange
StackOverflow
Bytes IT Community
Hay muchos más artículos, y después de leerlos, surgen algunos patrones.

Entonces, mi pregunta es ¿debería usar cadenas y números mágicos en nuestro código? Estoy buscando específicamente respuestas de expertos que estén respaldadas por referencias si es posible.

Daniel Gruszczyk
fuente
2
Una variable mágica es una variable que tiene un significado que no se refleja en su contenido. El valor entero '10' refleja el significado del número 10, por lo que no es necesario convertirlo en una constante. Lo mismo ocurre con el espacio y el punto y coma. Por otro lado, si tiene un valor '%% ?? %%' y este es un delimitador personalizado, entonces DEBE colocarse como una constante porque su contenido no refleja el hecho de que es un delimitador.
Jeroen Vannevel
23
NumberTen = 10Eso no tiene sentido ya que el número 10 no será redefinido. MaxRetryCount = 10Eso tiene un punto a que podemos querer cambiar el recuento máximo de reintentos. private const char SemiColon = ';'; Tonto. private const char LineTerminator = ';'; Inteligente.
Mike
1
La pregunta real no está clara.
Tulains Córdova

Respuestas:

89

... cuando discutía con uno de mis colegas, quien iba a declarar constantes como:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

El argumento que necesita hacer con su colega no se trata de nombrar un espacio literal como Spacesino de su mala elección de nombre para sus constantes.

Digamos que el trabajo de su código es analizar una secuencia de registros que contienen campos separados por punto a;b;cy coma ( ) y están separados por espacios ( a;b;c d;e;f). Si quien escribió su especificación lo llama dentro de un mes y le dice "nos equivocamos, los campos en los registros están separados por símbolos de tubería ( a|b|c d|e|f)", ¿qué hace?

Según el esquema de valor como nombre que prefiere su colega, tendría que cambiar el valor del literal ( SemiColon = '|') y vivir con el código que continúa utilizando SemiColonpara algo que ya no es punto y coma. Eso conducirá a comentarios negativos en las revisiones de código . Para disminuir eso, puede cambiar el nombre del literal a PipeSymboly pasar y cambiar cada aparición de SemiColona PipeSymbol. A ese ritmo, también podría haber usado un punto y coma literal ( ';') en primer lugar, porque tendrá que evaluar cada uso individualmente y realizará la misma cantidad de cambios.

Identificadores para las constantes tienen que ser descriptivo de lo que el valor lo hace , no lo que el valor es , y ahí es donde su colega ha hecho un giro a la izquierda en la maleza. En la aplicación de división de campo descrita anteriormente, el propósito del punto y coma es un separador de campo, y las constantes deben nombrarse en consecuencia:

private const char FieldSeparator = ';';    // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;

De esta manera, cuando cambia el separador de campo, cambia exactamente una línea de código, la declaración de la constante. Alguien que vea el cambio verá solo esa línea e inmediatamente comprenderá que el separador de campo cambió de un punto y coma a un símbolo de tubería. El resto del código, que no necesitaba cambiar porque estaba usando una constante, sigue siendo el mismo, y el lector no tiene que buscarlo para ver qué más se le hizo.

Blrfl
fuente
Estoy totalmente de acuerdo. Hace unas décadas trabajé en un proyecto donde los mensajes se enviaban en segmentos, cada uno con 8 registros generales. Alguien había declarado, #define one 1 #define two 2 etc. (o el equivalente en la oficina de correos del Reino Unido Coral, el idioma elegido). Se corrió la #define one 8 #define two 16
voz de
3
Tan tonto como nombres como punto y coma o PipeSymbol parecen, el cambio de una a otra mediante un script va a ser mucho más fácil que cambiar todos los afectados ;a |.
Brandin
¿Qué pasa con el caso donde un literal de cadena dado se usa muchas veces en un archivo, pero no tiene otro significado que su valor? Por ejemplo, si está probando que puede recibir cierta clave en un mapa en 20 escenarios de diferencia, ¿debería definir una constante como esta ? public static final String MY_KEY_NAME = "MyKeyName"
Jordan McQueen
1
@JordanMcQueen Hay un caso para usar literales desnudos si (y solo si) cada uno se usa exactamente una vez y no se necesita en ningún otro lugar. Si se trata de algo así como cada código de ser escenario que procesa un formato de archivo diferente, cada formato debe definir su propia constante (por ejemplo, CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATOR, etc.).
Blrfl
8

Definir punto y coma como una constante es redundante, porque el punto y coma ya es constante por sí mismo . No va a cambiar nunca.

No es como si un día alguien anunciara "cambio de terminología, + es el nuevo punto y coma ahora", y su colega se apresurará a actualizar la constante (se rieron de mí, mírelos ahora).

También hay una cuestión de consistencia. Le garantizo que su NumberTenconstante NO será utilizada por todos (la mayoría de los codificadores no están locos), por lo que de todos modos no servirá para el propósito que se esperaba. Cuando llega el apocalipsis y "diez" se redimensiona globalmente a 9, actualizar la constante NO será el truco, porque todavía te dejará con un montón de 10s literales en tu código, por lo que ahora el sistema se vuelve totalmente impredecible incluso dentro del alcance de una suposición revolucionaria de que "diez" significa "9".

Almacenar todas las configuraciones como consts es algo en lo que también tengo dudas. Uno no debe hacer esto a la ligera.

¿Qué ejemplos de este tipo de uso hemos reunido hasta ahora? Terminador de línea ... recuento máximo de reintentos ... número máximo de ruedas ... ¿estamos seguros de que nunca cambiarán?

El costo es que cambiar la configuración predeterminada requiere volver a compilar una aplicación y, en algunos casos, incluso sus dependencias (ya que los valores numéricos constantes pueden codificarse durante la compilación).

También está el aspecto de prueba y burla. Definió la cadena de conexión como una constante, pero ahora no puede burlarse del acceso a la base de datos (establecer una conexión falsa) en la prueba de su unidad.

Konrad Morawski
fuente
44
"No va a cambiar nunca". Solía ​​pensar eso sobre el apóstrofe (siempre vinculado al valor ASCII 39). Algunas aplicaciones antiguas solían enroscar el apóstrofe. Pero ahora las aplicaciones modernas tratan ese valor ASCII como un apóstrofe directo compatible con aplicaciones antiguas, y en su lugar las personas suelen usar (comilla simple izquierda, Unicode 8217 ) para aplicaciones compatibles con mostrar un glifo diferente para la marca curva. Siendo que Europa usa comas de la misma manera que los estadounidenses usan puntos como punto decimal, me siento un poco indeciso al declarar "no ... nunca".
TOOGAM
@TOOGAM así, tu ejemplo justifica tener una DecimalPointconstante - pero no Commao Periodconstantes. Es una gran diferencia: el primero denota una función , un rol o un propósito del valor. "Punto y coma" o "coma" no entran en esa categoría.
Konrad Morawski
Eso es cierto para el ejemplo de punto decimal. Sin embargo, el ejemplo del apóstrofe parece ser una categoría similar (o idéntica) a una coma (o punto y coma).
TOOGAM
@KonradMorawski Semicolon podría usarse para muchos propósitos, como dividir la cadena o terminar la línea. Es su significado (no su valor) lo que debe usarse para nombrar la constancia. Considere el cambio futuro, es decir, mañana permitiremos que se procesen 20 registros, por lo que la constancia denominada NumberTen está fuera de contexto, mientras que maxRecord todavía estaría bien.
MaxZoom
5
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Por lo tanto, su colega apunta a una entrada diaria de WTF. Esas definiciones son tontas y redundantes. Sin embargo, como han señalado otros, las siguientes definiciones no serían tontas ni redundantes:

private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int  BalanceInquiryCode = 10;

Los números y cadenas "mágicos" son constantes que tienen un significado más allá de su valor literal inmediato. Si la constante 10tiene un significado más allá de "diez cosas" (digamos como un código para una operación específica o condición de error), es cuando se convierte en "mágico" y debe ser reemplazado por una constante simbólica que describa ese significado abstracto.

Más allá de describir claramente la intención, las constantes simbólicas también te ahorran algunos dolores de cabeza cuando escribes mal un literal. Una simple transposición de "CVS" a "CSV" en una línea de código pasó por las pruebas unitarias y el control de calidad y llegó a producción, donde causó el fallo de una operación en particular. Sí, obviamente la unidad y las pruebas de control de calidad estaban incompletas y ese es su propio problema, pero el uso de una constante simbólica habría evitado esa acidez estomacal por completo.

John Bode
fuente
3

No debería haber nada controvertido al respecto. El punto no es si usar números mágicos o no, el punto es tener un código legible.
Considere la diferencia entre: if(request.StatusCode == 1)y if(request.HasSucceeded). En este caso, diría que este último es mucho más legible, pero eso no significa que nunca pueda tener un código similar int MaxNumberOfWheels = 18.

PD: Por eso odio absolutamente las pautas de codificación. Los desarrolladores deben ser lo suficientemente maduros como para ser capaces de hacer juicios de este tipo; no deberían dejarlo a un texto formado por Dios sabe quién.

Stefan Billiet
fuente
13
Los conductores deben ser lo suficientemente maduros como para ser capaces de juzgar en qué lado de la carretera conducen;)
Konrad Morawski
2
El resultado de una llamada de juicio puede variar incluso entre desarrolladores maduros, por lo que incluso las pautas de codificación arbitrarias están destinadas a mejorar la legibilidad a través de la coherencia. Esto no está relacionado con el hecho de que crear un NumberTen constante no tiene sentido.
Mike Partridge
1
No insistiría en que tienen que ser formales, estampados, etc., pueden ser informales, pero deben ser acordados, y esto ya va más allá del simple uso de la madurez individual del juicio. Pero eliminó su comentario ahora Stefan :)
Konrad Morawski
1
@StefanBilliet, para nada. Mi punto es que la legibilidad se mejora a través de la consistencia. El problema aquí no es la directriz de codificación en sí, sino una directriz llevada al extremo por malentendidos.
Mike Partridge
@ MikePartridge Quizás debería haber explicado; Las pautas de codificación que he visto están más en la tendencia de un libro de reglas generales sobre cómo alguien en alguna parte pensó que el software debería ser escrito, en lugar de acuerdos como usted y Konrad probablemente estén pensando en :-)
Stefan Billiet