Pregunta

Similar a Es difícil de codificar los literales aceptable?, pero estoy específicamente el pensamiento de "cadenas mágicas" aquí.

En un proyecto grande, tenemos una tabla de configuración de las opciones como estas:

Name         Value
----         -----
FOO_ENABLED  Y
BAR_ENABLED  N
...

(Cientos de ellos).

La práctica común es llamar a una función genérica para probar una opción como esta:

if (config_options.value('FOO_ENABLED') == 'Y') ...

(Por supuesto, esta misma opción puede necesitar ser examinada en muchos lugares en el código del sistema.)

Cuando se agrega una nueva opción, yo estaba pensando en añadir una función para ocultar la "magia" cadena como esta:

if (config_options.foo_enabled()) ...

Sin embargo, colegas pensaron que me había ido por la borda y se opuso a hacer esto, prefiriendo la codificación disco duro porque:

  • Eso es lo que hacemos normalmente
  • Esto hace que sea más fácil ver lo que está pasando cuando la depuración del código

El problema es que yo puedo ver su punto!De manera realista, que nunca lo vamos a cambiar el nombre de las opciones, por cualquier motivo, así que la única ventaja que se me ocurre para mi función es que el compilador de la captura de cualquier error tipográfico como fo_enabled(), pero no 'FO_ENABLED'.

¿Qué te parece?He perdido cualquier otras ventajas/desventajas?

¿Fue útil?

Solución

if (config_options.isTrue('FOO_ENABLED')) {...
}

Restrinja su cheque en Y codificado a un lugar, incluso si eso significa escribir una clase de contenedor para su mapa.

if (config_options.isFooEnabled()) {...
}

Puede parecer correcto hasta que tenga 100 opciones de configuración y 100 métodos (así que aquí puede hacer un juicio sobre el crecimiento y las necesidades futuras de la aplicación antes de decidir sobre su implementación). De lo contrario, es mejor tener una clase de cadenas estáticas para los nombres de parámetros.

if (config_options.isTrue(ConfigKeys.FOO_ENABLED)) {...
}

Otros consejos

Si uso una cadena una vez en el código, generalmente no me preocupo por convertirla en una constante en alguna parte.

Si uso una cadena dos veces en el código, lo consideraré convirtiéndolo en una constante.

Si uso una cadena tres veces en el código, es casi seguro que la convierta en una constante.

Me doy cuenta de que la pregunta es antigua, pero surgió en mi margen.

AFAIC, el problema aquí no se ha identificado con precisión, ni en la pregunta ni en las respuestas. Olvídate de 'harcoding strings & Quot; o no, por un momento.

  1. La base de datos tiene una tabla de referencia que contiene config_options. El PK es una cadena.

  2. Hay dos tipos de PK:

    • Identificadores significativos, que los usuarios (y desarrolladores) ven y usan. Se supone que estas PK son estables, se puede confiar en ellas.

    • Columnas Id sin sentido que los usuarios nunca deberían ver, que los desarrolladores deben conocer y codificar. No se puede confiar en estos.

  3. Es normal, normal, escribir código usando el valor absoluto de una PK significativa IF CustomerCode = "IBM" ... o IF CountryCode = "AUS" etc.

    • hacer referencia al valor absoluto de una PK sin sentido no es aceptable (debido al incremento automático; se modifican las brechas; los valores se reemplazan al por mayor).
      .
  4. Su tabla de referencia utiliza PK significativas. Hacer referencia a esas cadenas literales en el código es inevitable. Ocultar el valor dificultará el mantenimiento; el código ya no es literal; Tus colegas tienen razón. Además, existe la función redundante adicional que mastica los ciclos. Si hay un error tipográfico en el literal, pronto lo descubrirá durante las pruebas de desarrollo, mucho antes de UAT.

    • cientos de funciones para cientos de literales es absurdo. Si implementa una función, entonces Normalice su código y proporcione una sola función que pueda usarse para cualquiera de los cientos de literales. En cuyo caso, volvemos a un literal desnudo, y la función se puede prescindir.

    • el punto es que el intento de ocultar el literal no tiene valor.
      .

  5. No puede interpretarse como & "; codificación &"; eso es algo muy diferente. Creo que ahí es donde está su problema, identificando estas construcciones como & "; Codificado &"; Simplemente hace referencia a un PK significativo literalmente.

  6. Ahora, desde la perspectiva de cualquier segmento de código solamente, si usa el mismo valor varias veces, puede mejorar el código capturando la cadena literal en una variable y luego usando la variable en el resto del bloque de código Ciertamente no es una función. Pero ese es un problema de eficiencia y buenas prácticas. Incluso eso no cambia el efecto IF CountryCode = @cc_aus

En mi experiencia, este tipo de problema es enmascarar un problema más profundo:el fracaso para hacer real la programación orientada a objetos y seguir el principio SECO.

En pocas palabras, la captura de la decisión a la hora de inicio de una definición apropiada para cada acción dentro de el if declaraciones, y, a continuación, tirar tanto de la config_options y el tiempo de ejecución de pruebas.

Los detalles a continuación.

El uso de muestra fue:

if (config_options.value('FOO_ENABLED') == 'Y') ...

lo cual plantea la pregunta obvia, "Lo que está pasando en el botón de puntos suspensivos?", especialmente dada la siguiente declaración:

(Por supuesto, esta misma opción puede necesitar ser examinada en muchos lugares en el código del sistema.)

Supongamos que cada uno de estos config_option los valores que realmente no se corresponden con un único dominio del problema (o estrategia de implementación) concepto.

En vez de hacer esto (en repetidas ocasiones, en diversos lugares de todo el código):

  1. Tome una cadena (tag),
  2. Encuentre su correspondiente de la otra cadena (valor),
  3. Prueba de ese valor como un valor de tipo boolean-equivalente,
  4. Basado en la prueba, decidir si se debe realizar alguna acción.

Sugiero encapsular el concepto de una "acción configurable".

Vamos a tomar como ejemplo (obviamente solo como hypthetical como FOO_ENABLED ...;-) que el código tiene que trabajar en unidades inglesas o métricas unidades.Si METRIC_ENABLED es "true", convertir los datos introducidos por el usuario de métrico a inglés para el cálculo interno, y convertir de nuevo antes de mostrar los resultados.

Definir una interfaz:

public interface MetricConverter {
    double toInches(double length);
    double toCentimeters(double length);
    double toPounds(double weight);
    double toKilograms(double weight);
}

que identifica en un solo lugar todos los comportamientos asociados con el concepto de METRIC_ENABLED.

A continuación, escribir implementaciones concretas de todas las formas en que esos comportamientos han de llevarse a cabo:

public class NullConv implements MetricConverter {
    double toInches(double length) {return length;}
    double toCentimeters(double length) {return length;}
    double toPounds(double weight)  {return weight;}
    double toKilograms(double weight)  {return weight;}
}

y

// lame implementation, just for illustration!!!!
public class MetricConv implements MetricConverter {
    public static final double LBS_PER_KG = 2.2D;
    public static final double CM_PER_IN = 2.54D
    double toInches(double length) {return length * CM_PER_IN;}
    double toCentimeters(double length) {return length / CM_PER_IN;}
    double toPounds(double weight)  {return weight * LBS_PER_KG;}
    double toKilograms(double weight)  {return weight / LBS_PER_KG;}
}

En el tiempo de inicio, en lugar de cargar un montón de config_options valores, inicializar un conjunto de acciones configurables, como en:

MetricConverter converter = (metricOption()) ? new MetricConv() : new NullConv();

(donde la expresión metricOption() encima es un stand-in para cualquier cosa que uno sólo de verificación que debe realizar, incluyendo mirando el valor de METRIC_ENABLED ;-)

Entonces, dondequiera que el código habría dicho:

double length = getLengthFromGui();
if (config_options.value('METRIC_ENABLED') == 'Y') {
    length = length / 2.54D;
}
// do some computation to produce result
// ...
if (config_options.value('METRIC_ENABLED') == 'Y') {
    result = result * 2.54D;
}
displayResultingLengthOnGui(result);

volver a escribir como:

double length = converter.toInches(getLengthFromGui());
// do some computation to produce result
// ...
displayResultingLengthOnGui(converter.toCentimeters(result));

Porque todos los detalles de la implementación relacionados a un mismo concepto, ahora están empaquetados de forma limpia, todas las futuras relacionadas con el mantenimiento a METRIC_ENABLED se puede hacer en un solo lugar.Además, el tiempo de ejecución de trade-off es una victoria;la "sobrecarga" de la invocación de un método es trivial en comparación con la sobrecarga en la obtención de un valor de Cadena a partir de un Mapa y la realización de la Cadena de#iguala.

Creo que las dos razones que ha mencionado, Posible falta de ortografía en la cadena, que no se pueden detectar hasta el tiempo de ejecución y la posibilidad (aunque escasa) de un cambio de nombre justificaría su idea.

Además de eso, puede obtener funciones escritas, ahora parece que solo almacena booleanos, ¿qué pasa si necesita almacenar un int, una cadena, etc. Prefiero usar get_foo () con un tipo, que get_string ( quot; FOO ") o get_int (" FOO ").

Realmente debería usar constantes y no literales codificados.

Puedes decir que no se cambiarán, pero es posible que nunca sepas. Y es mejor hacerlo un hábito. Para usar constantes simbólicas.

Creo que hay dos problemas diferentes aquí:

  • En el proyecto actual, la convención de usar cadenas codificadas ya está bien establecida, por lo que todos los desarrolladores que trabajan en el proyecto están familiarizados con él. Puede ser una convención subóptima por todas las razones que se han enumerado, pero todos los que estén familiarizados con el código pueden verlo e instintivamente saben lo que se supone que debe hacer el código. Cambiando el código para que en ciertas partes, use & Quot; new & Quot; la funcionalidad hará que el código sea un poco más difícil de leer (porque la gente tendrá que pensar y recordar lo que hace la nueva convención) y, por lo tanto, un poco más difícil de mantener. Pero supongo que cambiar todo el proyecto a la nueva convención podría ser extremadamente costoso, a menos que pueda escribir rápidamente la conversión.
  • En un proyecto nuevo , las constantes simbólicas son IMO, por todos los motivos enumerados. Especialmente porque cualquier cosa que haga que el compilador detecte errores en tiempo de compilación que de otro modo sería detectado por un humano en tiempo de ejecución es una convención muy útil para establecer.

Yo también prefiero una clase de configuración fuertemente tipada si se usa en todo el código. Con métodos correctamente nombrados no pierdes ninguna legibilidad. Si necesita hacer conversiones de cadenas a otro tipo de datos (decimal / float / int), no necesita repetir el código que realiza la conversión en varios lugares y puede almacenar en caché el resultado para que la conversión solo se realice una vez. Ya tiene la base de esto en su lugar, así que no creo que sea necesario acostumbrarse a la nueva forma de hacer las cosas.

Otra cosa a considerar es la intención. Si está en un proyecto que requiere localización, las cadenas codificadas pueden ser ambiguas. Considere lo siguiente:

const string HELLO_WORLD = "Hello world!";
print(HELLO_WORLD);

La intención del programador es clara. El uso de una constante implica que esta cadena no necesita ser localizada. Ahora mira este ejemplo:

print("Hello world!");

Aquí no estamos tan seguros. ¿El programador realmente no quería que esta cadena se localizara o se olvidó de la localización mientras escribía este código?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top