¿Es una buena práctica tener un valor especial "ALL" en una enumeración


11

Estoy desarrollando un nuevo servicio en un entorno de microservicios. Este es un servicio REST. Por simplicidad, digamos que la ruta es: / historyBooks

Y el método POST para este camino crea un nuevo libro de historia.

Supongamos que un libro de historia cubre una o más épocas de la historia.

Por brevedad, supongamos que solo tenemos las siguientes eras de la historia humana:

  • Antiguo
  • Post Clásico
  • Moderno

En mi código, me gustaría representarlos en un enum.

El cuerpo del método (la carga útil) está en formato JSON y debe incluir un nombre de campo eras. Este campo es una lista de eravalores que cubre este libro.

El cuerpo puede verse así:

{
  "name": "From the cave to Einstein - a brief history review",
  "author": "Foo Bar",
  "eras": ["Ancient", "Post Classical", "Modern"]
}

En este servicio específico, la lógica de negocios es:
Si no se proporcionan eras en la entrada, entonces se considera que este libro cubre todas las eras.

En la revisión de la API, se hizo una sugerencia:
incluir otro valor, ALLpara la enumeración de eras, para indicar explícitamente que todas las eras están cubiertas.

Creo que tiene algunas ventajas y desventajas.

Pros:

Entrada explícita

Contras:

Si se proporcionan dos elementos en la lista, diga ALLy Ancient: ¿qué se tomará de la aplicación? Supongo que eso ALLdebería anular los otros valores, pero esa es una nueva lógica de negocios.

Si ejecuto una consulta, para libros que cubren épocas específicas, ¿cómo representaría un libro que cubre todas las épocas? Si ALLtambién se usa para la salida (usando la misma lógica), entonces es responsabilidad del consumidor interpretar ALLcomo ["Ancient", "Post Classical", "Modern"].

Mi pregunta

Creo que tener lo nuevo ALLcausa más confusión que no tenerlo en absoluto.

¿Qué piensas? ¿Agregaría este ALLvalor o mantendría su API sin él?


77
¿Qué sucede si decides que quieres agregar la era atómica? ¿Los libros que cubren "todas" las épocas mágicamente obtienen nuevo contenido? ¿Empiezas a mentir sobre el contenido de los libros? ¿Pasas y actualizas todo? Eso podría no ser trivial si algunos libros ya tienen contenido sobre la era atómica.
8bittree

Respuestas:


13

Depende de si sus eras disponibles están disponibles para la aplicación de llamada. Presumiblemente lo son para que el usuario pueda seleccionar lo que le interesa. Si ese es el caso, entonces es un problema inicial proporcionar una opción "TODOS". Si fuera yo, tendría que enviar una lista de todas las eras en lugar de una opción de "todos". Si no, entonces necesita una opción "TODO" y corre el riesgo de que el front end recupere las cosas de una era que no entenderá.

Como señala, TODO con otras opciones significa que puede obtener solicitudes en conflicto. Una consideración adicional es que puede pasar "TODOS" y luego cualquier otra enumeración se convierte en exclusiones en lugar de inclusiones, por ejemplo, "TODOS", "Antiguo" significa "Todo excepto los Antiguos". Eso tiene algún tipo de sentido, pero obviamente la interfaz de usuario debe reflejar lo que puede ser demasiado complejo.

TL; DR; En su mayoría, "TODO" es una interfaz de usuario agradable y puede lograr lo mismo en el nivel de servicio sin ambigüedad, así que no lo haga.


6

Si no se proporcionan eras en la entrada, se considera que este libro cubre todas las eras.

Esto, y también tener un caso especial 'TODOS', es malo en mi humilde opinión: sus API deben ser explícitas siempre que sea posible. Tener casos especiales como 'nada realmente significa todo' significa que cualquier persona que consuma su API también debe conocer todos sus casos especiales o, como con el caso 'TODOS', conducir a solicitudes donde la intención y / o el resultado son ambiguos.

Los casos especiales también a menudo conducen a un código más complejo, tanto en términos de validación de si la entrada del usuario es válida o no, como también de manejar la solicitud correctamente.


1

Para las variables categóricas, evitaría poner un comodín "todos" en el mismo nivel.

Parece que tiene un criterio de búsqueda de dos niveles para el período de tiempo:

  1. boolean, is_selective?
  2. conjunto, disyunción de categorías específicas

La persona que llama puede especificar (falso, ignorado) o (verdadero, {'antiguo', 'moderno'}).

O puede elegir interpretar el conjunto vacío como comodín, denota No selectivo.

Para su caso específico, parece que tiene una variable continua, el año, que se discretiza con unos pocos valores conocidos, y lo que realmente quiere es hacer una aritmética de intervalos. Por lo tanto, sus consultas aceptarían un rango de años (inicio, fin) y las enumeraciones podrían proporcionar convenientemente dichos atributos.


1

tl; dr
Para su pregunta real, con respecto a las estructuras de datos locales en la implementación, estoy de acuerdo con su conclusión: evite un valor especial de todas las épocas porque en su mayoría agrega complejidad y oportunidades para cometer errores. Sin embargo, especialmente la IU del usuario final y tal vez los datos de carga útil serializados pueden ser historias diferentes.

Estructuras de datos locales

Miremos esto desde una perspectiva de sistema de tipos. Básicamente, una enumeración es un tipo que define un conjunto disjunto de categorías específicas (enumeradores), como ya se señaló en la respuesta de @JH . Una variable del tipo enum contiene exactamente una de esas categorías. Si desea representar una colección de valores de enumerador, necesita un segundo tipo:

// C++-inspired pseudo-code
enum Era { ancient, post_classical, modern };
using Eras = Collection<Era>;

Un allenumerador es un no-ir porque combina esos dos tipos juntos. No es una categoría única, sino una colección de todas las categorías posibles.

En un nivel estrictamente práctico, lidiar con cualquier tipo de valor especial complica la implementación y, por lo tanto, aumenta la probabilidad de errores, ya sea que tiene que hacer un caso especial en todas partes o desempaquetarlo para que coincida con su significado real. Ah, y qué hay de una colección explícita de todos los enumeradores posibles. ¿Cuándo y dónde debería colapsarse con el allvalor especial ? ¿Debería estar colapsado?

Mi arquitectura preferida es no tener ninguna representación de todas las eras en el código. Eso incluye el allenumerador, pero también una colección vacía que significa todas las eras . Es exactamente el mismo caso especial, solo que con un disfraz diferente.

Si se proporcionan dos elementos en la lista, diga ALL y Ancient: ¿qué se tomará de la aplicación? [...]

Especificaría en la especificación de API que el marcador de todas las eras siempre debe aparecer por sí solo porque tener algo encima no tiene sentido. Entonces rechazaría esto como una violación del contrato. Pero es un buen ejemplo de cómo todas las eras complican tanto la implementación como la lógica empresarial.

El principal problema es que está obligado a especificar reglas para la conversión entre el valor especial y su significado subyacente. Y luego, usted y todos los demás que usan la API deben implementar esas reglas correctamente. El cínico en mí me dice que no se trata de si alguien se equivocará, sino simplemente cuándo .

Datos serializados (JSON)

Originalmente tenía una sección completa aquí sobre la modificación de consultas de solo lectura y diferentes roles para la "eras"lista. Pero al final lo eliminé porque KISS. Las reglas para enum / collection no son irrazonables para los datos JSON, por lo que mantener las cosas consistentes es la solución más simple.

IU para el usuario final

Supongo que habrá una transformación de datos de todos modos entre la interfaz de usuario y las estructuras de datos subyacentes, muy probablemente porque tiene algún tipo de arquitectura MVC-ish. Por lo tanto, use lo que sea más conveniente e intuitivo para el usuario. En su respuesta, @Markus dio un gran ejemplo con las diferentes opciones de selección para los días de la semana.


1

Creo que tener el nuevo ALL causa más confusión que no tenerlo en absoluto.

Si.

Si piensas desde una perspectiva de colección: tienes una colección completa de algo. Y cada vez que solo desea un subconjunto de esa colección, debe proporcionar algún tipo de condición de filtro , cuando un elemento se considera un elemento de este subconjunto. Y ALLes el caso, cuando no se aplica ningún filtro, no "la condición del filtro es ALL" .

Si no se proporcionan eras en la entrada, se considera que este libro cubre todas las eras.

Lo pondría al revés: este libro no cubre una era específica .

Esto también es consistente desde una perspectiva de consulta:

Si no se selecciona ninguna era, se devuelven todos los libros (incluido esto con un campo de era vacío); y cuando se selecciona una era, solo se devuelven los libros con la era seleccionada: recuperar todos los libros de una era especial más los generales sería de alguna manera inesperado.

El único punto en el que agregaría la ALLcategoría - es para conveniencia del usuario, porque puede ser más irritante tener "Nada" seleccionado en lugar de "Todo".


0

Recientemente implementé un planificador básico y, como ya mencioné @LoztInSpace, la mayor parte es azúcar de interfaz de usuario.

La interfaz de usuario debe ser absolutamente clara en lo que el usuario logrará al seleccionar una de las opciones.

En mi caso tengo días de semana para seleccionar. Además de "Todos", agregué "Días laborables" y "Fin de semana" como opciones. Al seleccionar cada opción, se incluirá en un uso posterior y deberá anular la selección de forma manual los días que no desee incluir.

Técnicamente, esto significa que si el usuario selecciona "Días de la semana", la IU tendrá cinco casillas de verificación marcadas y la enumeración usará el valor de "Días hábiles". Si una de las casillas de verificación no está marcada, el valor de "Días hábiles" se reemplaza con un mapa de bits de los cuatro días restantes.

No use una parte de las opciones para incluir todo y, al mismo tiempo, excluir algo para evitar conflictos y asegurarse de que la IU tenga sentido para el usuario.

Creo que una buena orientación es que usar y la opción "Todos" tiene sentido si el usuario puede comprender fácilmente el concepto de lo que incluye "Todos" (todos los días de una semana) o si no es importante (busque todas las categorías en Amazon)


0

Me gusta la idea de llevar explícitamente una enumeración ALL, pero prefiero establecerlos como banderas

const enum = {
    ALL: { value: 0 },
    ANCIENT: { name: 'Ancient', value: 1 },
    POST_CLASSICAL: { name: 'Post Classical', value: 2 },
    MODERN: { name: 'Modern', value: 4 }
}

Usando una biblioteca como flagon o jugando manualmente con operaciones bit a bit cuando se seleccionan todos los valores, usa ALL en su lugar.

Tenga en cuenta que los valores de la enumeración siempre deben ser el doble de su predecesor. Y eso para verificar la cordura nunca debe eliminar una enumeración, aunque puede deshabilitarla, ajustando su código para contar siempre las deshabilitadas como parte de ALL.

Aunque en su lugar tendría un indicador NINGUNO y permitiría al cliente decidir qué hacer cuando se use NINGUNO, en caso de que el negocio cambie más tarde.

Si se toma esta implementación, en lugar de pasar las enuns, pasaría la suma de los valores seleccionados.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.