2009-04-14 21 views
70

He visto varias preguntas/discusiones aquí sobre la mejor manera de manejar y persistir enum-como valores (por ejemplo, Persisting data suited for enums, How to persist an enum using NHibernate), y me gustaría preguntar cuál es el consenso general .Mejores prácticas para usar y persistir enumeraciones

En particular:

  • ¿Cómo debe ser manejado estos valores en el código?
  • ¿Cómo deberían persistir en una base de datos (como texto/como número)?
  • ¿Cuáles son las ventajas y desventajas de las diferentes soluciones?

Nota: Pasé las explicaciones originalmente incluidas en esta pregunta a una respuesta.

Respuesta

3

He intentado resumir mi comprensión. No dude en editar esto si tiene alguna corrección. Así que aquí va:

En el código

En el código, enumeraciones deben ser manejados usando cualquiera de los tipos enum nativo del idioma (por lo menos en Java y C#), o el uso de algo así como el "typesafe enum pattern". No se recomienda el uso de constantes simples (entero o similar), ya que se pierde la seguridad del tipo (y dificulta la comprensión de qué valores son datos legales, por ejemplo, un método).

La elección entre estos dos depende de la cantidad de funcionalidad adicional se va a unir a la enumeración:

  • Si usted quiere poner un montón de funcionalidad en la enumeración (lo cual es bueno, porque se evita el interruptor () ing todo el tiempo), una clase suele ser más apropiada.
  • Por otro lado, para valores sencillos como enum, la enumeración del lenguaje suele ser más clara.

En particular, por lo menos en una enumeración de Java no puede heredar de otra clase, por lo que si usted tiene varias enumeraciones con un comportamiento similar que le gustaría poner en una superclase, no se puede utilizar enumeraciones de Java.

enumeraciones La persistencia de

a persistir enumeraciones, cada valor de enumeración se deben asignar un identificador único. Esto puede ser un número entero o una cadena corta. Se prefiere una cadena corta, ya que puede ser mnemónica (hace que sea más fácil para los DBA, etc., comprender los datos sin procesar en el db).

  • En el software, cada enum debe entonces tener funciones de mapeo para convertir entre el enum (para uso en el interior del software) y el valor de ID (por persistir). Algunos marcos (por ejemplo, (N) Hibernate) tienen un soporte limitado para hacer esto automáticamente. De lo contrario, debes ponerlo en el tipo/clase enum.
  • La base de datos debe contener (idealmente) una tabla para cada enumeración que enumere los valores legales. Una columna sería la ID (ver arriba), que es la PK. Las columnas adicionales pueden tener sentido para, por ejemplo, una descripcion. Todas las columnas de la tabla que contendrán valores de esa enumeración pueden entonces usar esta "tabla de enumeración" como FK. Esto garantiza que los valores de enum incorrectos nunca pueden persistir, y permite que el DB "se soporte por sí mismo".

Un problema con este enfoque es que la lista de valores de enum legales existe en dos lugares (código y base de datos). Esto es difícil de evitar y, por lo tanto, a menudo se considera aceptable, pero hay dos alternativas:

  • Solo conserve la lista de valores en el DB, genere el tipo de enumeración en tiempo de compilación. Elegante, pero significa que se requiere una conexión de base de datos para que se ejecute una construcción, lo que parece problemático.
  • Defina la lista de valores en el código para ser autoritativo. Verifique los valores en el DB en tiempo de ejecución (generalmente al inicio), se queje/aborte cuando no coinciden.
2

En mi humilde opinión, como para la parte de código:

Debe siempre utilizar el tipo 'enumeración' para sus enumeraciones, básicamente se obtiene una gran cantidad de regalos si lo hace: Seguridad de tipos, encapsulación y la evitación del interruptor, el soporte de algunas colecciones como EnumSet y EnumMap y claridad de código.

En cuanto a la parte de persistencia, siempre puede persistir la representación de cadena de la enumeración y cargarla de nuevo utilizando el método enum.valueOf (String).

+0

De acuerdo en principio, sin embargo, al menos en Java "enum" es limitado en que no puede tener una superclase (como se señaló anteriormente), por lo que a veces una clase "enum de tipo seguro" es probablemente mejor. – sleske

3

Almacenar el valor de texto de una enumeración en una base de datos es menos preferido que almacenar un número entero, debido al espacio adicional requerido y la búsqueda más lenta. Es valioso en el sentido de que tiene más significado que un número, sin embargo, la base de datos es para el almacenamiento, y la capa de presentación es para que las cosas se vean bien.

+1

No se garantiza que el valor int de la enumeración sea el mismo a lo largo del tiempo. –

+0

Además, si usa una cadena corta, el rendimiento debería ser el mismo. Un char (2) toma 2 bytes, un int generalmente también toma 2 o 4. – sleske

+6

@Miguel Ping: La idea es * explícitamente * asignar un ID (int o char) a cada enumeración. Usar el int generado internamente de la enumeración es de hecho muy peligroso. – sleske

5

Java o C# siempre deben usar enumeraciones en el código. Descargo de responsabilidad: Mi fondo es C#.

Si el valor debe persistir en una base de datos, los valores integrales de cada miembro de enumeración deben definirse explícitamente para que un cambio posterior en el código no altere accidentalmente los valores enum traducidos y por lo tanto el comportamiento de la aplicación.

Los valores siempre se deben conservar en una base de datos como valores integrales, para proteger contra la refactorización del nombre enum. Guarde la documentación de cada enumeración en una wiki y agregue un comentario al campo de la base de datos que apunta a la página wiki que documenta el tipo. También agregue documentación XML al tipo de enumeración que contiene un enlace a la entrada wiki para que esté disponible a través de Intellisense.

Si usa una herramienta para generar código CRUD, debería ser capaz de definir un tipo de enumeración para usar en una columna, de modo que los objetos de código generados siempre usen los miembros enumerados.

Si lógica personalizada se necesita aplicar para un miembro de la enumeración, usted tiene algunas opciones:

  • Si usted tiene una MyEnum enumeración, crear un MyEnumInfo clase estática que ofrece métodos de utilidad para descubrir información adicional acerca de la enumeración miembro, por declaraciones de interruptor o lo que sea necesario. Agregar "Información" al final del nombre enum en el nombre de la clase asegura que estarán uno al lado del otro en IntelliSense.
  • Decore los miembros de la enumeración con atributos para especificar parámetros adicionales. Por ejemplo, hemos desarrollado un control EnumDropDown que crea una ASP.NET desplegable lleno de valores de enumeración, y un EnumDisplayAttribute especifica el texto de visualización muy bien formateado para usar para cada miembro.

No he probado esto, pero con SQL Server 2005 o posterior, que en teoría podría registrar el código C# con la base de datos que contiene información de enumeración y la capacidad de convertir los valores de las enumeraciones para su uso en puntos de vista u otras construcciones, hacer un método de traducción de los datos de una manera más fácil para que los DBAs lo usen.

+0

+1 asignar valores explícitamente es la única manera de evitar "corrupción" cuando cambia la enumeración – ashes999

5

En el manejo de código para C#, se ha perdido la definición de acarrear el valor 0. Casi siempre sin falta declarar mi primer valor como:

public enum SomeEnum 
{ 
    None = 0, 
} 

para que sirva como un valor nulo. Debido a que el tipo de respaldo es un número entero y un entero por defecto es 0, por lo que es enormemente útil en muchos lugares para saber si una enumeración ha sido programada o no.

+5

No estoy de acuerdo. Esto solo tendría sentido si a veces dejas las variables sin inicializar, lo que consideraría seriamente una mala práctica. A menudo he visto esta idea de tener un valor "ninguno", pero creo que solo oculta el problema real (la variable no inicializada). – sleske

+2

¿Cómo se oculta el problema? Lo hace explícito como un int nullable. Dejo los valores sin inicializar en el código porque sé lo que el CLR establecerá por defecto. Todavía están inicializados es solo implícito. – Quibblesome

+0

Bueno, es probablemente una cuestión de estilo. Creo firmemente en la inicialización total de todas las variables en la declaración (o como máximo en un if-else directamente después de la declaración). De lo contrario, puede olvidarse de inicializarlos, especialmente si el flujo de código es complicado. Ver también http://c2.com/cgi/wiki?SingleStepConstructor. – sleske

3

Bueno, desde mi experiencia, usar enums para cualquier cosa que no sea para pasar opciones (como banderas) a una llamada inmediata al método, da como resultado switch -ing en algún momento.

  • Si va a utilizar la enumeración de todo su código, entonces usted podría terminar con el código que no es tan fácil de mantener (la infame declaración switch)
  • enumeraciones La ampliación es un dolor. Agregas un nuevo ítem enum y terminas revisando todo tu código para verificar todas las condiciones.
  • Con .NET 3.5, puede agregar métodos de extensión a enumeraciones para hacer que se comporten un poco más como clases. Sin embargo, la adición de la funcionalidad real de esta manera no es tan fácil, ya que todavía no es una clase (que terminaría usando switch -es en sus métodos de extensión, si no en otra parte.

Así que para una entidad enumeración-como con un poco más de funcionalidad que debe tomar algún tiempo y crearla como una clase, con varias cosas en mente:

  • para hacer que su clase se comporta como una enumeración, puede obligar a cada clase derivada para crear una instancia como Singleton, o anular Iguala para permitir la comparación de valores de diferentes instancias.
  • Si tu clase es enum, debería significar tha t no debe contener ningún estado serializable; la deserialización debería ser posible solo por su tipo (una especie de "ID", como dijiste).
  • La lógica de persistencia debe limitarse a la clase base solamente, de lo contrario, la extensión de su "enumeración" sería una pesadilla. En caso de que eligiera el patrón Singleton, necesitaría asegurar una deserialización adecuada en instancias singleton.
3

Cada vez que te encuentres usando "números mágicos" en el cambio de código en enumeraciones. Además de ahorrar tiempo (ya que la magia desaparecerá cuando aparezcan los errores ...) le ahorrará los ojos y la memoria (las enumeraciones significativas hacen que el código sea más legible y autodocumentado), ya que adivine qué: usted es probablemente la persona que debe mantener y desarrollar su propio código

12

El artículo inicial se ve bien para mí. Aún así, según los comentarios, parece que algunos comentarios sobre las enumeraciones de Java podrían aclarar algunas cosas.

Enum type en Java es una clase por definición, pero muchos programadores tienden a olvidar esto, porque lo relacionan con "una lista de valores permitidos" como en otros idiomas. Es más que eso.

Por lo tanto, para evitar esas instrucciones de cambio, podría ser razonable poner algún código y métodos adicionales en la clase enum. Casi nunca hay necesidad de crear una "clase real enum" separada.

Considere también el punto de la documentación: ¿desea documentar el significado real de su enumeración en la base de datos? ¿En el código fuente que refleja los valores (su tipo de enumeración) o en alguna documentación externa? Yo personalmente prefiero el código fuente.

Si desea presentar valores enum como enteros en la base de datos debido a la velocidad o el motivo, esa asignación también debe residir en la enumeración de Java. Obtendrá la asignación de nombre de cadena de forma predeterminada, y he estado satisfecho con eso. Hay un número ordinal asociado con cada valor enum, pero usarlo directamente como una asignación entre código y base de datos no es muy brillante, porque ese número ordinal cambiará si alguien reordena los valores en el código fuente. O agrega valores enum adicionales entre los valores existentes. O elimina algún valor.

(Por supuesto, si alguien cambia el nombre de la enumeración en el código fuente, el mapeo de cadenas predeterminado también se agriará, pero es menos probable que ocurra accidentalmente) y puede protegerlo más fácilmente si es necesario poniendo algunas restricciones de verificación y comprobación de tiempo de ejecución en la base de datos, tal como se sugiere aquí.)

+1

Existen dos escenarios para respaldar: alguien que reordena las enumeraciones en mi archivo O alguien que realiza algunas refactorizaciones (para aclarar las malas selecciones iniciales de nombres) y rompe los datos persistentes. Creo que este último es más importante, y ordinal es el camino a seguir para la persistencia de los datos. – Justin

18

Estoy de acuerdo con mucho de lo que dice. Una cosa que me gustaría añadir, sin embargo, sobre la persistencia de las enumeraciones: no creo que la generación de las enumeraciones en tiempo de compilación a partir de los valores DB sea aceptable, pero también creo que el control del tiempo de ejecución no es una buena solución . Definiría un tercer medio: tener una prueba de unidad que verificará los valores de la enumeración contra la base de datos. Esto evita la divergencia "casual" y evita la sobrecarga de verificar las enumeraciones en la base de datos cada vez que se ejecuta el código.

1

Sé que este es un foro antiguo, ¿y si la base de datos pudiera tener otras cosas integradas directamente a ella? P.ej. cuando el DB resultante es el ÚNICO propósito del código. Entonces, definirás las enumeraciones en cada integración. Mejor entonces tenerlos en la base de datos. De lo contrario, estoy de acuerdo con la publicación original.

Cuestiones relacionadas