È una buona pratica avere un valore speciale "ALL" in un enum


11

Sto sviluppando un nuovo servizio in un ambiente di micro-servizi. Questo è un servizio REST. Per semplicità, diciamo che il percorso è: / historyBooks

E il metodo POST per questo percorso crea un nuovo libro di storia.

Supponiamo che un libro di storia copra una o più epoche della storia.

Per brevità, supponiamo di avere solo le seguenti epoche della storia umana:

  • Antico
  • Post classica
  • Moderno

Nel mio codice, vorrei rappresentarli in un enum.

Il corpo del metodo (il payload) è in formato JSON e dovrebbe includere un nome campo eras. Questo campo è un elenco di eravalori trattati da questo libro.

Il corpo può apparire come:

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

In questo servizio specifico, la logica aziendale è:
se non sono state fornite epoche nell'input, questo libro è considerato per coprire tutte le epoche.

Nella revisione dell'API è stato formulato un suggerimento:
includere un altro valore ALL, per l'era delle epoche, per indicare esplicitamente che tutte le epoche sono coperte.

Penso che abbia alcuni pro e contro.

Professionisti:

Input esplicito

Contro:

Se vengono forniti due elementi nell'elenco, dire ALLe Ancient- cosa verrà preso dall'applicazione? Immagino che ALLdovrebbe prevalere sugli altri valori, ma questa è una nuova logica aziendale.

Se eseguo una query, per i libri che trattano epoche specifiche, come rappresenterei i libri che coprono tutte le epoche? Se ALLviene utilizzato anche per l'output (utilizzando la stessa logica), è responsabilità del consumatore interpretare ALLcome ["Ancient", "Post Classical", "Modern"].

La mia domanda

Penso che avere il nuovo ALLcausi più confusione che non averlo affatto.

Cosa ne pensi? Aggiungeresti questo ALLvalore o manterrai la tua API senza di essa?


7
Cosa succede se decidi di aggiungere l'era atomica? I libri che coprono "tutte" le epoche ottengono magicamente nuovi contenuti? Inizi a mentire sul contenuto dei libri? Passi attraverso e aggiorni tutto? Ciò potrebbe non essere banale se alcuni libri in realtà hanno già dei contenuti sull'era atomica.
8

Risposte:


13

Dipende se le tue epoche disponibili sono disponibili per l'applicazione chiamante. Presumibilmente lo sono in modo che l'utente possa selezionare ciò a cui sono interessati. In tal caso, è un problema di front-end fornire un'opzione "ALL". Se fossi in me, avrebbe inviato un elenco di tutte le epoche anziché un'opzione "tutto". Altrimenti, hai bisogno di un'opzione "TUTTO" e corri il rischio che il front-end riporti le cose da un'era che non capirà.

Come sottolineato, TUTTO con altre opzioni significa che è possibile ottenere richieste in conflitto. Un'ulteriore considerazione è che puoi passare "TUTTI", quindi qualsiasi altro enum diventa esclusione anziché inclusione, ad esempio "TUTTO", "Antico" significa "Tutto tranne gli Antichi". Ciò ha un certo senso, ma ovviamente l'interfaccia utente deve riflettere ciò che potrebbe essere troppo complesso.

TL; DR; Per lo più "ALL" è un'interfaccia utente gradevole e puoi ottenere la stessa cosa a livello di servizio senza ambiguità, quindi non farlo.


6

Se non sono state fornite epoche nell'input, questo libro considera tutte le epoche.

Questo, e anche con un caso speciale "TUTTO", sono un cattivo IMHO - le tue API dovrebbero essere esplicite dove possibile. Avere casi speciali come "nulla in realtà significa tutto" significa che chiunque consuma la tua API deve anche conoscere tutti i tuoi casi speciali o, come nel caso del "TUTTO", portare a richieste in cui l'intento e / o il risultato sono ambigui.

Casi speciali spesso portano anche a codici più complessi sia in termini di convalida se l'input dell'utente è valido o meno, sia per gestire correttamente la richiesta.


1

Per le variabili categoriali, eviterei di mettere un jolly "all" allo stesso livello.

Sembra che tu abbia un criterio di ricerca a due livelli per il periodo di tempo:

  1. booleano, is_selective?
  2. impostare, disgiunzione di categorie specifiche

Il chiamante può specificare (falso, ignorato) o (vero, {"antico", "moderno"}).

Oppure potresti scegliere di interpretare un set vuoto come jolly, che indica Non selettivo.

Per il tuo caso specifico, sembra che tu abbia una variabile continua, l'anno, che è discretizzata su alcuni valori ben noti, e quello che vuoi davvero è fare l'aritmetica degli intervalli. Quindi le tue query accetterebbero un intervallo di (inizio, fine) di anni e gli enum potrebbero fornire convenientemente tali attributi.


1

tl; dr
Per la tua vera domanda - riguardante le strutture di dati locali nell'implementazione - concordo con la tua conclusione: evitare un valore speciale per tutte le epoche perché aggiunge principalmente complessità e opportunità di errori. Tuttavia, in particolare l'interfaccia utente dell'utente finale e forse i dati di payload serializzati possono essere storie diverse.

Strutture di dati locali

Diamo un'occhiata a questo dal punto di vista del sistema di tipo. Fondamentalmente un enum è un tipo che definisce un insieme disgiunto di categorie specifiche (enumeratori), come già sottolineato nella risposta di @JH . Una variabile del tipo enum contiene esattamente una di quelle categorie. Se si desidera rappresentare una raccolta di valori di enumeratore, è necessario un secondo tipo:

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

Un allenumeratore è un no-go perché unisce questi due tipi insieme. Non è una singola categoria, ma una raccolta di tutte le possibili categorie.

A livello strettamente pratico, gestire qualsiasi tipo di valore speciale complica l'implementazione - e quindi aumenta la probabilità di errori - perché è necessario eseguirne il caso speciale dappertutto o decomprimerlo per corrispondere al suo significato reale. Oh, e che dire di una raccolta esplicita di tutti i possibili enumeratori. Quando e dove dovrebbe essere compresso questo allvalore speciale ? Dovrebbe essere crollato affatto?

La mia architettura preferita è quella di non avere alcuna rappresentazione di tutte le epoche nel codice. Ciò include l' allenumeratore, ma anche una raccolta vuota che significa tutte le epoche . È esattamente lo stesso caso speciale, solo con un travestimento diverso.

Se vengono forniti due elementi nell'elenco, dire TUTTO e Antico: cosa verrà preso dall'applicazione? [...]

Specificherei nelle specifiche API che il marker di tutte le epoche deve sempre apparire da solo perché avere qualcosa sopra non ha senso. Quindi rifiuterei questo come violazione del contratto. Ma è un bell'esempio di come tutte le epoche complichino sia l'implementazione che la logica aziendale.

Il problema principale è che sei costretto a specificare le regole per la conversione tra il valore speciale e il significato sottostante. E poi tu e tutti gli altri che usano l'API dovete implementare queste regole correttamente. Il cinico in me mi dice che non è una questione se qualcuno sbaglierà, ma semplicemente quando .

Dati serializzati (JSON)

Inizialmente avevo un'intera sezione qui sulla modifica di query di sola lettura e ruoli diversi per l' "eras"elenco. Ma alla fine l'ho cancellato perché KISS. Le regole per enum / collection non sono irragionevoli per i dati JSON, quindi mantenere le cose coerenti è la soluzione più semplice.

UI per l'utente finale

Presumo che ci sarà comunque una trasformazione dei dati tra l'interfaccia utente e le strutture di dati sottostanti, molto probabilmente perché hai un qualche tipo di architettura MVC. Quindi, utilizza ciò che è più conveniente e intuitivo per l'utente. Nella sua risposta @Markus ha dato un ottimo esempio con le diverse opzioni di selezione per i giorni della settimana.


1

Penso che avere il nuovo ALL causi più confusione che non averlo affatto.

Sì.

Se pensi dal punto di vista della collezione: hai un'intera collezione di qualcosa. E ogni volta che vuoi solo un sottoinsieme di quella raccolta, devi fornire un qualche tipo di filtro , quando un elemento è considerato un elemento di questo sottoinsieme. Ed ALLè il caso, quando non viene applicato alcun filtro, non "la condizione del filtro è ALL" .

Se non sono state fornite epoche nell'input, questo libro considera tutte le epoche.

Lo capovolgerei: questo libro non copre un'era specifica .

Ciò è coerente anche dal punto di vista della query:

Se non viene selezionata alcuna era, vengono restituiti tutti i libri (incluso questo con un campo di era vuoto); e quando viene selezionata un'era, vengono restituiti solo i libri con l'era selezionata: recuperare tutti i libri da un'era speciale più quelli generali sarebbe in qualche modo inaspettato.

L'unico punto in cui aggiungerei la ALLcategoria - è per comodità dell'utente, perché potrebbe essere più irritante avere "Niente" selezionato anziché "Tutto".


0

Di recente ho implementato uno scheduler di base e come @LoztInSpace ha già menzionato la maggior parte di esso è lo zucchero dell'interfaccia utente.

L'interfaccia utente deve essere assolutamente chiara in ciò che l'utente otterrà selezionando una delle opzioni.

Nel mio caso ho giorni feriali da selezionare. Oltre a "Tutti" ho aggiunto "Giorni lavorativi" e "Fine settimana" come opzioni. Selezionando ciascuna opzione la includerai in un uso successivo e dovrai deselezionare manualmente tutti i giorni che non vuoi includere.

Tecnicamente questo significa che se l'utente seleziona "Giorni feriali", l'interfaccia utente avrà cinque caselle di spunta e l'enum utilizzerà il valore "Giorni lavorativi". Se una delle caselle di controllo è deselezionata, il valore "Giorni lavorativi" viene sostituito con una mappa di bit or-ed dei restanti quattro giorni.

Non utilizzare una parte delle opzioni per includere tutto e allo stesso tempo escludere qualcosa per evitare conflitti e assicurarsi che l'interfaccia utente abbia senso per l'utente.

Penso che un buon orientamento sia che usare e l'opzione "Tutto" ha senso se l'utente può facilmente comprendere il concetto di cosa include "Tutto" (tutti i giorni della settimana) o se non è importante (cerca in tutte le categorie su Amazon)


0

Mi piace l'idea di portare esplicitamente un enumerazione ALL, ma preferirei impostarli come bandiere

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

Usando una libreria come flagon o giocando manualmente con operazioni bit a bit quando sono selezionati tutti i valori, invece si utilizza ALL.

Tieni presente che i valori dell'enum dovrebbero sempre essere il doppio del loro predecessore. E che per il controllo della sanità mentale non dovresti mai rimuovere un enum, sebbene tu possa disabilitarlo, regolando il tuo codice per contare sempre quelli disabilitati come parte di TUTTI.

Anche se avrei invece un flag NESSUNO e consentirei al cliente di decidere cosa fare quando viene utilizzato NESSUNO, nel caso in cui l'attività cambi in seguito.

Se si prende questa implementazione, invece di passare i enuns in giro, si passerebbe invece la somma dei valori selezionati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.