Supongo que esta es otra pregunta sobre la codificación rígida y las mejores prácticas. Digamos que tengo una lista de valores, digamos fruta, almacenados en la base de datos (debe estar en la base de datos ya que la tabla se usa para otros fines, como los informes SSRS), con una ID:
1 Apple
2 Banana
3 Grapes
Puedo presentarlos al usuario, selecciona uno, se almacena en su perfil como FavouriteFruit y la ID almacenada en su registro en la base de datos.
Cuando se trata de reglas de negocio / lógica de dominio, ¿cuáles son las recomendaciones para asignar lógica a valores específicos? Digamos que si el usuario ha seleccionado Grapes, deseo realizar alguna tarea adicional, ¿cuál es la mejor manera de hacer referencia al valor de Grapes?
// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")
// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes
// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)
¿o algo mas?
Como, por supuesto, FavouriteFruit se utilizará en toda la aplicación, la lista se puede agregar o editar.
Alguien puede decidir que quiere que 'Grapes' cambie su nombre a 'Grape' y, por supuesto, esto rompería la opción de cadena codificada.
Sin embargo, la ID codificada no está completamente clara, como se muestra, podría agregar un comentario para identificar rápidamente qué elemento es.
La opción enum implica la duplicación de datos de la base de datos, lo que parece incorrecto, ya que puede no estar sincronizado.
De todos modos, gracias de antemano por cualquier comentario o sugerencia.
MyApplication.Grape.ID
tartamudea, por así decirlo. Una "Apple" no es una "Red_Apple", como tampoco el ID de 3 es 4. Por lo tanto, el potencial para cambiar el nombre de "Apple" a "Red_Apple" no tiene más sentido que declarar que 3 es 4 (y tal vez incluso 3). El objetivo de una enumeración es abstraer su ADN numérico. Entonces, tal vez es hora de desacoplar realmente las claves de base de datos relacionales arbitrarias que literalmente no tienen significado en los modelos de negocio de uno.Respuestas:
Evite cadenas y constantes mágicas a toda costa. Están completamente fuera de discusión, ni siquiera deberían considerarse como opciones. Esto parece dejarle solo una opción viable: identificadores, es decir, enumeraciones. Sin embargo, también hay una opción más, que en mi opinión es la mejor. Llamemos a esta opción "Objetos precargados". Con los objetos precargados, puede hacer lo siguiente:
Lo que acaba de suceder aquí es que obviamente he cargado toda la fila
Grape
en la memoria, por lo que tengo su ID lista para usar en las comparaciones. Si está utilizando el mapeo relacional de objetos (ORM), se verá aún mejor:(Por eso lo llamo "Objetos precargados").
Entonces, lo que hago es que durante el inicio cargue todas mis tablas de "enumeración" (tablas pequeñas como días de la semana, meses del año, géneros, etc.) en la clase de dominio de la aplicación principal. Los cargo por nombre, porque obviamente,
MyApplication.Grape
debo recibir la fila llamada "Grape", y afirmo que se encuentran todos y cada uno de ellos. De lo contrario, tenemos un error de tiempo de ejecución garantizado durante el inicio, que es el menos maligno de todos los errores de tiempo de ejecución.fuente
Grape = fetchRow( Fruit.class, NameColumn, "Grape" );
Y si usted hacer algo incorrectamente, unAssertionError
testamento te lo hará saber.enum
lo que hubiera sido una cadena mágica. El punto es la concentración de todos los enlaces por nombre en un solo lugar , la validación de todo durante el inicio y la seguridad de tipo .La comprobación de la cadena es la más legible, pero cumple una doble función: se usa tanto como identificador como descripción (que puede cambiar por razones no relacionadas).
Por lo general, divido ambas tareas en campos separados:
Donde la descripción puede cambiar (pero no "Grapes" a "Banana"), pero el código no puede cambiar, nunca.
Aunque esto se debe principalmente a que nuestros identificadores casi siempre se generan automáticamente y, por lo tanto, no encajan bien. Si puede elegir los identificadores libremente, tal vez pueda garantizar que siempre sean correctos y usarlos.
Además, ¿con qué frecuencia alguien realmente edita "Grapes" a "Grape"? Quizás nada de esto sea necesario.
fuente
Lo que espera aquí es que la lógica de programación se adapte automáticamente a los datos cambiantes. Las opciones estáticas simples como Enum no funcionan aquí porque no es posible agregar enumeraciones adicionales en el tiempo de ejecución.
Algunos patrones que he visto:
En general, me gusta que los datos se completen en términos de referirse a las acciones que implica, a pesar de que las acciones en sí mismas podrían implementarse en otro lugar. Cualquier código que determine acciones independientes de los datos acaba de fragmentar su representación de datos, lo que probablemente divergerá y conducirá a errores.
fuente
Almacenarlos en ambos lugares (en una tabla y en un ENUM) no es tan malo. El razonamiento es el siguiente:
Al almacenarlos en una tabla de base de datos, podemos imponer integridad referencial en la base de datos a través de claves externas. Entonces, cuando asocia una persona o cualquier entidad a una fruta, solo existe una fruta en la tabla de la base de datos.
Almacenarlos como un ENUM también tiene sentido porque podemos escribir código sin cadenas mágicas y hace que el código sea más legible. Sí, tienen que mantenerse sincronizados, pero en realidad lo difícil que sería agregar una línea al ENUM y una nueva instrucción de inserción a la base de datos.
Una cosa, una vez que se define un ENUM, no cambie su valor. Por ejemplo, si tuvieras:
NO cambie el nombre de Uva a Uvas. Simplemente agregue un nuevo ENUM.
Si necesita migrar datos, aplique una actualización para mover toda la uva a uvas.
fuente
Tiene razón al hacer esta pregunta, en realidad es una buena pregunta, ya que intenta defenderse contra la evaluación de condiciones inexactas.
Dicho esto, la evaluación (sus
if
condiciones) no necesariamente tiene que ser el foco de cómo evitarla. En su lugar, preste atención a la forma en que propaga los cambios que causarían un problema 'fuera de sincronización'.Enfoque de cuerda
Si debe usar cadenas, ¿por qué no exponer la funcionalidad de cambiar la lista a través de la interfaz de usuario? Diseñe el sistema de modo que, al cambiar
Grape
aGrapes
, por ejemplo, actualice todos los registros que actualmente hacen referenciaGrape
.Enfoque de identificación
Siempre prefiero hacer referencia a una identificación, a pesar del compromiso de cierta legibilidad.
The list may be added to
nuevamente puede ser algo de lo que se le notificaría si expone dicha función de IU. Si le preocupa el reordenamiento de los elementos que cambian la identificación, vuelva a propagar dicho cambio a todos los registros dependientes. Similar a lo anterior. Otra opción (siguiendo la convención de normalización adecuada, sería tener una columna enum / id, y hacer referencia a unaFruitDetail
tabla más detallada , que tiene una columna 'Order' que puede buscar).De cualquier manera, puede ver que estoy sugiriendo controlar la modificación o actualización de su lista. Los detalles específicos de su tecnología determinan si hace esto mediante el uso de un ORM o algún otro acceso a datos. Lo que estás haciendo esencialmente es exigir a las personas que se alejen de la base de datos para tales cambios, lo que creo que está bien. La mayoría de los CRM principales harán el mismo requisito.
fuente
Un problema muy común. Si bien la duplicación del lado del cliente de datos puede parecer que viola los principios DRY , en realidad se debe a la diferencia de paradigma entre las capas.
Tener la enumeración (o lo que sea) fuera de sintonía con la base de datos tampoco es tan infrecuente. Es posible que haya introducido otro valor en una tabla de metadatos para admitir una nueva función de informes que aún no se utiliza en el código del lado del cliente.
A veces sucede a la inversa también. Se agrega un nuevo valor de enumeración al lado del cliente, pero la actualización de la base de datos no puede ocurrir hasta que el DBA pueda aplicar los cambios.
fuente
Suponiendo que estamos hablando de lo que es esencialmente una búsqueda estática, la tercera opción, la enumeración, es básicamente la única opción sensata. Es lo que haría si la base de datos no estuviera involucrada, por lo que tiene sentido.
La pregunta se convierte entonces en cómo sincronizar las enumeraciones y las tablas estáticas / de búsqueda en la base de datos y, desafortunadamente, ese no es un problema para el que todavía tengo una respuesta completa.
Por elección, hago todo el mantenimiento de mi esquema en código y, por lo tanto, puedo mantener una relación entre una compilación de la aplicación y una versión de esquema esperada, por lo que es sencillo mantener la búsqueda y la enumeración sincronizadas, pero es algo que uno debe recordar hacer. Sería mejor si fuera más automatizado (y también una prueba de integración automatizada para garantizar que las enumeraciones y las coincidencias de búsqueda ayudarían), pero eso no es algo que haya implementado.
fuente