¿Es una mala práctica almacenar ciertos valores como cadenas?

19

Es un título muy vago, pero no se me ocurre una mejor manera de expresarlo. Pero, solo como ejemplo, piense en la dirección en la que se mueve un personaje en un juego. Se siente un poco mal usar una cuerda y luego hacer cosas como if(character.direction == "left"). Me parece que deja demasiado margen para errores tontos, como el uso accidental Lefto lo lo que sea en lugar de left. ¿Son correctas mis sospechas? Si es así, ¿cuál es la forma preferida de lograr algo como esto?

Cal7
fuente
10
quieres decir, además de enumeraciones si tu idioma es compatible
hoffmale
12
¿Te refieres a Stringly Typed ?
RubberDuck
Algunos idiomas no son capaces de almacenar valores que no sean en forma de cadenas, por ejemplo, bash.
mouviciel
no sé por qué, pero recordé el "definir" en C. Puede hacer cosas como: #define false true
linuxunil

Respuestas:

28

Si el lenguaje que está utilizando admite el uso de enumeraciones, los usaría. Le permite limitar la cantidad de opciones disponibles para un tipo dado. Por ejemplo, en Java:

public enum Direction {
    LEFT,
    RIGHT,
    UP,
    DOWN
}
Quizás_Factor
fuente
2
Esta respuesta no contribuye a comprender el problema de OP; No veo ningún razonamiento aquí, solo una opinión limitada en su alcance a los idiomas con clase enumcomo java. Muchos idiomas (por ejemplo, VBA) tienen enum, pero puede que no sea la mejor opción ya que carece de la misma protección para el futuro. Vea mi respuesta para una discusión de por qué enumsolo es una solución incompleta, a menudo frágil y específica del idioma.
2016
12

Es una práctica terrible usar cadenas literales (o números mágicos) en el código.

Las enumeraciones son buenas, o al menos usan constantes (las enumeraciones, por supuesto, son solo un tipo envoltorio para una o más constantes relacionadas).

const string LEFT = "left"
if (someThing == LEFT) { doStuff(); }

Este simple cambio tiene tantas ventajas que ni siquiera sabría por dónde empezar a explicarlas (al menos no sin un poco más de café). Lea sobre los números mágicos, es el mismo concepto exacto, solo se aplica a un valor numérico en lugar de un valor de cadena.

Aquí hay una referencia rápida, estoy seguro de que hay cientos más: /programming/47882/what-is-a-magic-number-and-why-is-it-bad

jleach
fuente
1
El problema que tengo con este enfoque es que obtienes errores tipográficos como --const string LEFT = "letf" - mientras que si trabajas con enumeraciones que quedan atrapados en el momento de la compilación.
Pieter B
@PieterB: pensé que la pregunta de los OP era lo suficientemente amplia, y el ejemplo de "izquierda" es solo un ejemplo arbitrario, donde todos los casos no coincidirían con una enumeración. Estoy de acuerdo en que para un conjunto de direcciones específicamente, una enumeración es la mejor opción, pero mi respuesta tenía la intención de ser más genérica en que "si lo pones en un literal, al menos hazlo una constante" (enumeraciones, por supuesto, ser un tipo de constante)
jleach
3
Tuve el placer de trabajar con una aplicación que asignaba los días del fin de semana como aquellos días cuyo nombre comienza con una "s". Muy sorprendidos estaban por el "hecho" de que cuando internacionalizaron la solicitud, Francia solo tuvo un fin de semana de un día.
Pieter B
44
Llámelo microoptimización, pero algunos idiomas pueden interpretar esto como una comparación de valores y perder mucho tiempo comprobando cada carácter de la cadena. Aún más probable si haces lo que hizo el autor de la pregunta y lo comparas con la cadena codificada "izquierda". Si no es necesario que el valor constante sea una cadena (es decir, no lo imprime), sería mejor usar un const int en su lugar.
Devsman
4

Respuesta corta: Sí, las cadenas no son ideales para ninguna otra tarea que no sea almacenar y acceder a una secuencia de caracteres textuales, e incluso si los bits subyacentes de una abstracción son cadenas, hay ventajas en hacer referencia a ellas como variables o constantes .

Respuesta larga: la mayoría de los idiomas ofrecen tipos que están más cerca del dominio de su problema, e incluso si no lo hacen, probablemente tengan algún método por el cual puede definir leftcomo una entidad diferente de right(etc., etc.). Convertirlo en una cadena mediante el uso "left"es simplemente la pérdida del idioma y la funcionalidad útil del IDE, como la verificación de errores. Incluso si tienes que usar

left = "left";

o algo equivalente, tiene ventajas al usar la cadena cuando se refiere a ella a través de su código. Por ejemplo,

if (player.direction == Left)

se detectaría como un error en tiempo de compilación (el mejor de los casos, java y tal) o sería marcado por cualquier IDE que valga sus bits como incorrecto (el peor de los casos, básico y tal).

Además, tener la noción de leftentidad referenciable lo ayudará a adaptarse a cualquier dirección que decida ir con el tipo de dirección. Al usar "left", la dirección se compromete a ser a String. Si encuentra o crea una mejor abstracción para el tipo, debe buscar todo el cuerpo del código cambiando cada instancia de "left". Una constante o variable no requerirá ningún cambio si se elabora el tipo.

El tipo que realmente desea es particular para su dominio. ¿Qué planea hacer tu código con tu left? Tal vez desee reflejar el eje x de una textura o sprite que se enfrenta a la izquierda o se mueve hacia adelante; si es así, es posible que desee hacer leftun objeto con una propiedad o método que refleje ese comportamiento, con su rightobjeto teniendo el resultado opuesto no reflejado. Podría hacer esa lógica en un objeto que represente los sprites o texturas, en cuyo caso nuevamente sufrirá por usar en "left"lugar de leftpor las razones indicadas anteriormente.

En Java (y probablemente en otros lenguajes que aún no conozco), enumes una buena alternativa porque en Java enumes tan útil como final classcon un conjunto finito de instancias. Puede definir comportamientos si los necesita, y puede cambiar a una clase abierta sin problemas fuera del archivo que declara el enum, pero muchos idiomas ven un enumtipo primitivo o requieren una sintaxis especial para usar. Eso filtra el enumcapó al código que no tiene nada que ver con eso y es posible que deba corregirse más adelante. Asegúrese de comprender el concepto de idioma de su enumantes de considerar la opción.

sqykly
fuente
2

No especificó su idioma, pero para dar una respuesta genérica, debe usar cadenas cuando realmente necesita el texto, no para representar algo. El ejemplo que dio, por ejemplo, simplemente no sería aceptable en el software de producción.

Cada idioma tiene varios tipos de datos básicos y debe usar el más apropiado para la tarea. Para usar su ejemplo, podría usar constantes numéricas para representar diferentes estados. Entonces left podría tener un valor de cero y right sería uno. Además de la razón que mencionó, los valores numéricos ocupan menos espacio (memoria) y siempre es más rápido comparar números que comparar cadenas de caracteres múltiples.

Como se ha sugerido, use enumeraciones si su idioma lo permite. En su ejemplo específico, es claramente la mejor opción.

Nagev
fuente
Es poco probable que la velocidad y la memoria importen. Dicho esto, prefiera enumeraciones o constantes. Las cadenas tienen más sentido si los datos provienen de un formato de texto, por ejemplo, XML, pero aun así son consistentes con todas las mayúsculas o minúsculas. O, siempre convierta , por ejemplo. if (uppercase(foo) == "LEFT")
user949300
2
@ user949300 La conversión de cadenas a mayúsculas o minúsculas tampoco es confiable. Existe la posibilidad de que alguien decida visitar otro lugar (o expandir el negocio allí), como Turquía , y romper ingenuas comparaciones de cadenas de mayúsculas (o minúsculas).
8bittree
Preocuparse por la velocidad y la memoria siempre es una buena práctica, no son recursos infinitos. Aunque está bien sacrificarlo conscientemente en aras de la legibilidad o alguna otra compensación, que no es el caso aquí. Pero no preocuparse por ellos puede conducir fácilmente a aplicaciones lentas o derrochadoras.
Nagev
Es probable que comparar cadenas sea 50 veces más lento que comparar enumeraciones. La mayoría de las veces no importa. Pero si su código ya es un poco lento, esa diferencia puede hacerlo inaceptable. Más importante, por supuesto, es el hecho de que el compilador no puede ayudarlo con ningún error ortográfico si usa cadenas.
gnasher729
1

Simplemente use el tipo de datos que tenga sentido en su situación.

El uso del stringtipo como comunicación subyacente entre aplicaciones / intra aplicaciones no es un problema inherente. De hecho, a veces es preferible usar cadenas: si tiene una API pública, puede ser deseable que los valores que entran y salen de la API sean legibles para los humanos. Esto facilita que las personas que usan su API puedan aprender y depurar.

El verdadero problema con su código radica en repetir el valor.

No hagas esto:

if (direction == 'left') {
  goLeft();
}

Si cometió un error tipográfico en uno de estos condicionales u otros usos de "left" en alguna parte, es posible que no pueda realizar una búsqueda efectiva en todo el código base porque lo ha escrito mal.

En su lugar, codifique contra una enumeración o una constante, dependiendo de cuál sea el tipo de datos que necesita en los idiomas que está utilizando.

Eso significa que LEFTdebe asignarse a una vez y solo una vez en su aplicación. Y, el resto de su código debería aprovechar esas enumeraciones o constantes:

const string LEFT = "left";

if (direction == LEFT) {
  goLeft();
}

Esto también le permite cambiar más fácilmente el tipo de datos que usa para sus instrucciones más adelante, si así lo desea.

svidgen
fuente
1

¿Es una mala práctica?

Probablemente sí, pero depende exactamente de lo que esté haciendo y también del lenguaje de programación que esté utilizando.

En su ejemplo, podemos inferir que existe un conjunto de valores pequeño y fijo para siempre que una dirección podría tomar. En este caso, no hay ventajas en el uso de una cadena, y algunas desventajas definitivas. Para este ejemplo:

  • Un enumtipo sería preferible, si su lenguaje de programación lo admite.
  • De lo contrario, las constantes enteras con nombre son el camino a seguir, con una sola definición para las constantes.

Pero la respuesta puede ser diferente si el conjunto de valores se puede modificar dinámicamente o si necesita evolucionar con el tiempo. En la mayoría de los idiomas, cambiar un enumtipo (por ejemplo, para agregar o eliminar un valor) requiere una compilación completa. (Por ejemplo, eliminar un valor de enumeración en Java rompe la compatibilidad binaria). Y lo mismo se aplica a las constantes de enteros con nombre.

En escenarios como este, puede ser necesaria otra solución, y el uso de cadenas es una de las opciones. (Y podría combinar literales de cadena y constantes con nombre ... si el escenario implica un núcleo fijo con extensiones dinámicas).


Si está implementando en Java, character.direction == "left"es una mala práctica por otra razón. No debe usar ==para comparar cadenas, ya que prueba las identidades de los objetos en lugar de la igualdad de cadenas. (Funcionaría si pudiera garantizar que todas las instancias de la "left"cadena hayan sido internados, pero eso requiere un análisis completo de la aplicación).

Stephen C
fuente
1
Lo que parece arreglado para siempre a menudo resulta que no está tan arreglado en absoluto. Cuatro direcciones pueden, por ejemplo, convertirse en ocho.
Jimmy T.
@JimmyT. - Eso depende del contexto. Por ejemplo, en un juego, cambiar el número de direcciones que un personaje puede mover de 4 a 8 implicaría una revisión completa de las reglas del juego y el código que implementa las reglas. Usar Stringpara representar las direcciones supondría una diferencia cercana a cero en la cantidad de trabajo involucrado para realizar los cambios. Un enfoque más sensato es asumir que la cantidad de direcciones no cambiará y reconocer que tendría mucho trabajo por hacer de cualquier manera si las reglas del juego cambiaran tan fundamentalmente.
Stephen C
@JimmyT: totalmente de acuerdo. He visto que esto sucede muchas veces, también hace que los desarrolladores tomen el atajo de redefinir la cadena, por ejemplo, product = "Option" se convierte en product = "Option_or_Future", lo que degrada totalmente el significado del código.
Chris Milburn
-3

Como se sugiere, debe usar Enums. Sin embargo, solo usar Enum como reemplazo de Strigns no es suficiente en mi humilde opinión. En su ejemplo particular, la velocidad del jugador en realidad debería ser un vector por medio de la física:

class Vector {
  final double x;
  final double y;
}

Este vector debe usarse para actualizar la posición del jugador en cada marca.

Luego puede combinar eso con una enumeración para una mayor legibilidad:

enum Direction {
  UP(new Vector(0, 1)),
  RIGHT(new Vector(1, 0)),
  DOWN(new Vector(0, -1)),
  LEFT(new Vector(-1, 0));

  private Vector direction;

  Direction(Vector v) {
    this.direction = v;
  }

  public Vector getVector() { return this.direction; }
}
marstato
fuente
44
-1 Estás yendo demasiado lejos en el ejemplo que él da.
Pieter B