È una buona pratica avere un valore speciale "TUTTI" 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ù ere nella storia.

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

  • Antico
  • Post classico
  • Moderna

Nel mio codice, mi piacerebbe rappresentarli in un enum .

Il corpo del metodo (il payload) è in formato JSON e dovrebbe includere un nome di campo eras . Questo campo è un elenco di valori di era , che copre questo libro.

Il corpo può avere il seguente aspetto:

{
  "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 ci sono ere fornite nell'input, questo libro è considerato per coprire le tutte ere.

Nella revisione dell'API, è stato presentato un suggerimento:
Includere un altro valore, ALL , per l'ena di ena, per indicare esplicitamente che tutte le ere sono coperte.

Penso che abbia alcuni pro e contro.

Pro:

Input esplicito

Contro:

Se vengono forniti due elementi nell'elenco, ad esempio ALL e Ancient : quale sarà il risultato dell'applicazione? Immagino che ALL debba sostituire gli altri valori, ma questa è una nuova logica aziendale.

Se eseguo una query, per i libri che coprono periodi specifici, come potrei rappresentare un libro che copre tutte le ere? Se anche ALL viene utilizzato per l'output (utilizzando la stessa logica), è responsabilità del consumatore interpretare ALL come ["Ancient", "Post Classical", "Modern"] .

La mia domanda

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

Che ne pensi? Vuoi aggiungere questo valore di ALL o mantenere la tua API senza di essa?

    
posta Ron Klein 15.09.2017 - 07:50
fonte

7 risposte

13

Dipende dal fatto che le ere disponibili siano disponibili per l'applicazione chiamante. Presumibilmente sono così che l'utente può selezionare ciò a cui sono interessati. In tal caso, si tratta di un problema front-end per fornire un'opzione "ALL". Se fossi in me, avrebbe mandato un elenco di tutte le ere piuttosto che un'opzione "tutto". Se no, allora hai bisogno di un'opzione "TUTTI" e corri il rischio che il front-end riprenda le cose da un'era che non capirà.

Come indicato, TUTTO con altre opzioni significa che puoi ricevere richieste in conflitto. Un'ulteriore considerazione è che puoi passare "TUTTI" quindi qualsiasi altra enumerazione diventa esclusioni piuttosto che inclusioni, ad es. "ALL", "Ancient" significa "Everything except the Ancients". Questo ha un qualche senso, ma ovviamente l'interfaccia utente deve riflettere ciò che potrebbe essere troppo complesso.

TL; DR; Per lo più "TUTTI" è un dettaglio e un'intelligenza dell'interfaccia utente; puoi raggiungere la stessa cosa a livello di servizio senza ambiguità, quindi non farlo.

    
risposta data 15.09.2017 - 08:06
fonte
6

If no eras provided in the input, then this book is considered to cover all eras.

Questo, e avendo anche un caso speciale 'ALL', è sbagliato IMHO - le tue API dovrebbero essere esplicite dove possibile. Avere casi speciali come "niente in realtà significa tutto" significa che chiunque consuma la tua API deve anche conoscere tutti i casi speciali o, come nel caso di "TUTTI", portare a richieste in cui l'intento e / o l'esito sono ambigui.

Anche i casi speciali portano spesso a codice più complesso sia in termini di convalida se l'input dell'utente è valido o meno, sia anche gestendo correttamente la richiesta.

    
risposta data 15.09.2017 - 19:04
fonte
1

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

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

  1. booleano, è_selettivo?
  2. set, disgiunzione di categorie specifiche

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

O potresti scegliere di interpretare il set vuoto come carattere jolly, che indica Non selettivo.

Per il tuo caso specifico, sembra che tu abbia una variabile continua, l'anno, che è discretizzato per alcuni valori noti, e ciò che vuoi veramente è fare l'aritmetica dell'intervallo. Quindi le tue domande accetterebbero un intervallo (di inizio, fine) di anni, e le enumerazioni potrebbero convenientemente fornire tali attributi.

    
risposta data 15.09.2017 - 18:45
fonte
1

tl; dr
Per la tua domanda reale - riguardante le strutture di dati locali nell'implementazione - concordo con la tua conclusione: evita un valore speciale tutti i periodi perché in genere aggiunge complessità e opportunità per commettere errori. Tuttavia, specialmente l'interfaccia utente dell'utente finale e forse i dati del payload serializzato possono essere storie diverse.

Strutture dati locali

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

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

Un enumeratore all è un no-go perché elimina i due tipi insieme. Non è una singola categoria, ma una raccolta di tutte le possibili categorie.

A livello strettamente pratico, affrontare qualsiasi tipo di valore speciale complica l'implementazione - e quindi aumenta la probabilità di errori - perché o lo si deve trovare in un caso speciale dappertutto o lo si decomprime per adattarlo al suo reale significato. Oh, e che dire di una raccolta esplicita di tutti i possibili enumeratori. Quando e dove dovrebbe essere collassato allo speciale valore all ? Dovrebbe essere collassato?

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

If two items in the list are provided, say ALL and Ancient - what will be taken from the application? [...]

Specifico nelle specifiche API che l'indicatore tutti gli aster deve sempre apparire da solo perché avere qualcosa su di esso non ha senso. Quindi lo rifiuterei come violazione del contratto. Ma è un bell'esempio di come tutte le ere complica sia l'implementazione che la logica di business.

Il problema principale è che sei costretto a specificare regole per la conversione tra il valore speciale e il suo significato sottostante. E poi tu e tutti gli altri che usano l'API devi implementare correttamente quelle regole. Il cinico dentro di me mi dice che non è una questione di se qualcuno sbaglia, ma semplicemente quando .

Dati serializzati (JSON)

Originariamente avevo un'intera sezione qui sulla modifica di query di sola lettura e ruoli diversi per la lista "eras" . Ma alla fine l'ho cancellato perché KISS. Le regole per enum / raccolta 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 dati sottostanti, molto probabilmente perché hai una sorta di architettura MVC-ish. Quindi, usa ciò che è più conveniente e intuitivo per l'utente. In la sua risposta @ Markus ha dato un ottimo esempio con le diverse opzioni di selezione per i giorni della settimana.

    
risposta data 19.09.2017 - 13:50
fonte
1

I think that having the new ALL causes more confusion than not having it at all.

Sì.

Se pensi da una prospettiva di raccolta: hai un'intera collezione di qualcosa. E ogni volta che vuoi solo un sottoinsieme di quella raccolta, devi fornire una sorta di filtercondition , quando un elemento è considerato un elemento di questo sottoinsieme. E ALL è il caso, quando non viene applicato alcun filtro, non "la condizione del filtro è ALL " .

If no eras provided in the input, then this book is considered to cover all eras.

Lo capovolgerei: Questo libro tratta dell'era non specifica .

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

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

L'unico punto, dove aggiungerei la ALL - categoria è per comodità dell'utente, perché potrebbe essere più irritante scegliere "Niente" anziché "Tutto".

    
risposta data 19.09.2017 - 22:22
fonte
0

Ho recentemente implementato uno scheduler di base e come @LoztInSpace ha già menzionato la maggior parte dello zucchero UI.

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

Nel mio caso ho giorni della settimana da selezionare. Oltre a "Tutti" ho aggiunto "Giorni lavorativi" e "Fine settimana" come opzioni. La selezione di ciascuna opzione la includerà in un secondo momento e dovrai deselezionare manualmente tutti i giorni che non desideri includere.

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

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

Penso che un buon orientamento sia che l'uso e l'opzione "Tutto" abbia senso se l'utente può facilmente cogliere il concetto che cosa include "Tutto" (tutti i giorni in una settimana) o se è piuttosto irrilevante (cerca tutte le categorie su Amazon)

    
risposta data 15.09.2017 - 08:57
fonte
0

Mi piace l'idea di portare esplicitamente un enum ALL, ma preferirei impostarli come flag

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

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

Ricorda che i valori dell'enum dovrebbero sempre essere il doppio del loro predecessore. E per il controllo della sanità mentale non dovresti mai rimuovere un enumerro, anche se potresti disabilitarlo, aggiustando il tuo codice per contare sempre quelli disabilitati come parte di ALL.

Anche se avrei un flag NONE invece e lasciare che il client decida cosa fare quando NONE viene usato, nel caso in cui l'attività cambi in seguito.

Se questa implementazione è presa, invece di passare gli enun in giro, dovresti invece passare la somma dei valori selezionati.

    
risposta data 18.09.2017 - 19:56
fonte

Leggi altre domande sui tag