Ist es eine gute Praxis, einen speziellen Wert "ALL" in einer Aufzählung zu haben?


11

Ich entwickle einen neuen Dienst in einer Mikrodienstumgebung. Dies ist ein REST-Service. Nehmen wir zur Vereinfachung an, der Pfad lautet: / historyBooks

Die POST-Methode für diesen Pfad erstellt ein neues Geschichtsbuch.

Nehmen wir an, dass ein Geschichtsbuch eine oder mehrere Epochen der Geschichte abdeckt.

Nehmen wir der Kürze halber an, wir haben nur die folgenden Epochen der Menschheitsgeschichte:

  • Uralt
  • Postklassik
  • Modern

In meinem Code möchte ich sie in einem darstellen enum.

Der Hauptteil der Methode (die Nutzdaten) liegt im JSON-Format vor und sollte einen Feldnamen enthalten eras. Dieses Feld ist eine Liste von eraWerten, die in diesem Buch behandelt werden.

Der Körper kann aussehen wie:

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

In diesem speziellen Dienst lautet die Geschäftslogik:
Wenn in der Eingabe keine Epochen angegeben sind, wird davon ausgegangen, dass dieses Buch alle Epochen abdeckt .

In der API-Überprüfung wurde ein Vorschlag gemacht:
Fügen Sie ALLfür die Epochen-Aufzählung einen anderen Wert hinzu , um explizit anzugeben, dass alle Epochen abgedeckt sind.

Ich denke, es hat einige Vor- und Nachteile.

Vorteile:

Explizite Eingabe

Nachteile:

Wenn zwei Elemente in der Liste angegeben sind, sagen Sie ALLund Ancient- was wird aus der Anwendung entnommen? Ich denke, das ALLsollte die anderen Werte überschreiben, aber das ist eine neue Geschäftslogik.

Wie würde ich Bücher darstellen, die alle Epochen abdecken, wenn ich eine Abfrage für Bücher durchführe, die bestimmte Epochen abdecken? Wenn ALLauch für den Ausgang (mit der gleichen Logik) verwendet wird, dann ist es in der Verantwortung des Verbrauchers zu interpretieren ALLwie ["Ancient", "Post Classical", "Modern"].

Meine Frage

Ich denke, dass das Neue ALLmehr Verwirrung stiftet, als es überhaupt nicht zu haben.

Was denkst du? Würden Sie diesen ALLWert hinzufügen oder Ihre API ohne ihn behalten?


7
Was passiert, wenn Sie sich entscheiden, die Atomära hinzuzufügen? Erhalten die Bücher, die "alle" Epochen abdecken, auf magische Weise neuen Inhalt? Lügst du über den Inhalt der Bücher? Gehen Sie durch und aktualisieren Sie alles? Das könnte nicht trivial sein, wenn einige Bücher tatsächlich bereits Inhalte über die Atomzeit enthalten.
8bittree

Antworten:


13

Hängt davon ab, ob Ihre verfügbaren Epochen für die aufrufende Anwendung verfügbar sind. Vermutlich sind sie es, damit der Benutzer auswählen kann, woran er interessiert ist. Wenn dies der Fall ist, ist es ein Front-End-Problem, eine "ALL" -Option bereitzustellen. Wenn ich es wäre, würde es eine Liste aller Epochen senden und nicht eine "Alle" -Option. Wenn nicht, dann brauchen Sie eine "ALL" -Option und laufen Gefahr, dass das Frontend Dinge aus einer Zeit zurückbekommt, die es nicht versteht.

Wie Sie hervorheben, bedeutet ALL mit anderen Optionen, dass Sie widersprüchliche Anforderungen erhalten können. Eine weitere Überlegung ist, dass Sie "ALL" übergeben können, dann werden alle anderen Aufzählungen eher zu Ausschlüssen als zu Einschlüssen, z. B. "ALL". "Ancient" bedeutet "Alles außer den Alten". Das macht irgendwie Sinn, aber natürlich muss die Benutzeroberfläche dann das widerspiegeln, was möglicherweise zu komplex ist.

TL; DR; Meistens ist "ALL" eine nette Benutzeroberfläche und Sie können das Gleiche auf Service-Ebene ohne Mehrdeutigkeit erreichen, also tun Sie es nicht.


6

Wenn in der Eingabe keine Epochen angegeben sind, wird davon ausgegangen, dass dieses Buch alle Epochen abdeckt.

Dies und auch ein spezieller "ALL" -Fall sind meiner Meinung nach schlecht - Ihre APIs sollten nach Möglichkeit explizit sein. Sonderfälle wie "Nichts bedeutet eigentlich alles" bedeuten, dass jeder, der Ihre API verwendet, auch alle Ihre Sonderfälle kennen muss oder wie im Fall "ALL" zu Anfragen führt, bei denen die Absicht und / oder das Ergebnis nicht eindeutig sind.

Sonderfälle führen häufig auch zu komplexerem Code, sowohl hinsichtlich der Überprüfung, ob die Benutzereingabe gültig ist oder nicht, als auch hinsichtlich der korrekten Behandlung der Anforderung.


1

Bei kategorialen Variablen würde ich vermeiden, einen Platzhalter "alle" auf die gleiche Ebene zu setzen.

Es hört sich so an, als hätten Sie ein zweistufiges Suchkriterium für den Zeitraum:

  1. boolean, is_selective?
  2. Set, Disjunktion bestimmter Kategorien

Der Anrufer kann angeben (falsch, ignoriert) oder (wahr, {'alt', 'modern'}).

Oder Sie können einen leeren Satz als Platzhalter interpretieren, der nicht selektiv bedeutet.

Für Ihren speziellen Fall klingt es so, als hätten Sie eine stetige Variable, das Jahr, das auf einige bekannte Werte diskretisiert ist, und Sie möchten wirklich Intervallarithmetik ausführen. Ihre Abfragen akzeptieren also einen (Start-, End-) Bereich von Jahren, und Aufzählungen können solche Attribute bequem bereitstellen.


1

tl; dr
Für Ihre eigentliche Frage - bezüglich der lokalen Datenstrukturen in der Implementierung - stimme ich Ihrer Schlussfolgerung zu: Vermeiden Sie einen besonderen Wert für alle Epochen , da dies hauptsächlich die Komplexität und die Möglichkeit erhöht, Fehler zu machen. Insbesondere die Benutzeroberfläche des Endbenutzers und möglicherweise die serialisierten Nutzdaten können jedoch unterschiedliche Geschichten sein.

Lokale Datenstrukturen

Betrachten wir dies aus der Perspektive eines Typsystems. Grundsätzlich ist eine Aufzählung ein Typ, der eine disjunkte Menge bestimmter Kategorien (Enumeratoren) definiert, wie bereits in der Antwort von @JH ausgeführt . Eine Variable vom Aufzählungstyp enthält genau eine dieser Kategorien. Wenn Sie eine Sammlung von Enumeratorwerten darstellen möchten, benötigen Sie einen zweiten Typ:

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

Ein allEnumerator ist ein No-Go, weil er diese beiden Typen miteinander verbindet. Es ist keine einzelne Kategorie, sondern eine Sammlung aller möglichen Kategorien.

Auf einer streng praktischen Ebene erschwert der Umgang mit jeder Art von besonderem Wert die Implementierung - und erhöht so die Wahrscheinlichkeit von Fehlern -, da Sie ihn entweder überall als Sonderfall behandeln oder auspacken müssen, um seiner tatsächlichen Bedeutung zu entsprechen. Oh, und was ist mit einer expliziten Sammlung aller möglichen Enumeratoren? Wann und wo soll dies auf den Sonderwert allreduziert werden? Sollte es überhaupt zusammengebrochen werden?

Meine bevorzugte Architektur ist es, nicht alle Epochen im Code darzustellen . Dazu gehört der allEnumerator, aber auch eine leere Sammlung, die alle Epochen bedeutet . Es ist genau der gleiche Sonderfall, nur in einer anderen Verkleidung.

Wenn zwei Elemente in der Liste enthalten sind, sagen Sie ALL und Ancient - was wird aus der Anwendung entnommen? [...]

Ich würde in der API-Spezifikation angeben, dass der All-Epochen- Marker immer von selbst angezeigt werden muss, da es keinen Sinn macht, etwas darüber zu haben. Dann würde ich dies als Vertragsverletzung ablehnen. Aber es ist ein schönes Beispiel dafür, wie alle Epochen sowohl die Implementierung als auch die Geschäftslogik komplizieren.

Das Hauptproblem besteht darin, dass Sie Regeln für die Konvertierung zwischen dem speziellen Wert und seiner zugrunde liegenden Bedeutung angeben müssen. Und dann müssen Sie und alle anderen, die die API verwenden, diese Regeln korrekt implementieren. Der Zyniker in mir sagt mir, dass es nicht darum geht, ob jemand etwas falsch macht, sondern einfach wann .

Serialisierte Daten (JSON)

Ursprünglich hatte ich hier einen ganzen Abschnitt über das Ändern von Nur-Lese-Abfragen und verschiedene Rollen für die "eras"Liste. Aber am Ende habe ich es wegen KISS gelöscht. Die Regeln für Enum / Collection sind für die JSON-Daten nicht unangemessen. Daher ist es am einfachsten, die Konsistenz zu gewährleisten.

Benutzeroberfläche für den Endbenutzer

Ich gehe davon aus, dass es sowieso eine Datentransformation zwischen der Benutzeroberfläche und den zugrunde liegenden Datenstrukturen geben wird, höchstwahrscheinlich, weil Sie eine Art MVC-ähnliche Architektur haben. Verwenden Sie also das, was für den Benutzer am bequemsten und intuitivsten ist. In seiner Antwort gab @Markus ein großartiges Beispiel mit den verschiedenen Auswahlmöglichkeiten für die Wochentage.


1

Ich denke, dass das neue ALL mehr Verwirrung stiftet, als es überhaupt nicht zu haben.

Ja.

Wenn Sie aus einer Sammlungsperspektive denken: Sie haben eine ganze Sammlung von etwas. Und jedes Mal , wenn Sie nur eine Teilmenge dieser Sammlung möchten, müssen Sie eine Filterbedingung angeben , wenn ein Element als Element dieser Teilmenge betrachtet wird. Und ALList der Fall, wenn kein Filter angewendet wird, nicht "die Filterbedingung ist ALL" .

Wenn in der Eingabe keine Epochen angegeben sind, wird davon ausgegangen, dass dieses Buch alle Epochen abdeckt.

Ich würde es auf den Kopf stellen: Dieses Buch behandelt keine bestimmte Ära.

Dies ist auch aus Sicht der Abfrage konsistent:

Wenn keine Ära ausgewählt ist, werden alle Bücher zurückgegeben (einschließlich dieser mit einem leeren Ära-Feld). und wenn eine Ära ausgewählt wird, werden nur Bücher mit der ausgewählten Ära zurückgegeben - das Abrufen aller Bücher aus einer speziellen Ära sowie allgemeiner Bücher wäre irgendwie unerwartet.

Der einzige Punkt, an dem ich die ALLKategorie - hinzufügen würde, dient der Benutzerfreundlichkeit, da es möglicherweise irritierender ist, wenn "Nichts" anstelle von "Alles" ausgewählt wird.


0

Ich habe kürzlich einen einfachen Scheduler implementiert und wie @LoztInSpace bereits erwähnt hat, ist das meiste davon UI-Zucker.

Die Benutzeroberfläche muss absolut klar sein, was der Benutzer bei der Auswahl einer der Optionen erreichen wird.

In meinem Fall habe ich Wochentage zur Auswahl. Zusätzlich zu "Alle" habe ich "Arbeitstage" und "Wochenende" als Optionen hinzugefügt. Wenn Sie jede Option auswählen, wird sie später verwendet, und Sie müssen die Auswahl aller Tage, die Sie nicht einbeziehen möchten, manuell aufheben.

Technisch bedeutet dies, dass wenn der Benutzer "Wochentage" auswählt, auf der Benutzeroberfläche fünf Kontrollkästchen aktiviert sind und die Aufzählung den Wert "Arbeitstage" verwendet. Wenn eines der Kontrollkästchen deaktiviert ist, wird der Wert "Arbeitstage" durch eine Bit-Map der verbleibenden vier Tage ersetzt.

Verwenden Sie nicht einen Teil der Optionen, um alles einzuschließen und gleichzeitig etwas auszuschließen, um Konflikte zu vermeiden und sicherzustellen, dass die Benutzeroberfläche für den Benutzer sinnvoll ist.

Ich denke, eine gute Orientierung ist, dass die Verwendung der Option "Alle" sinnvoll ist, wenn der Benutzer das Konzept, das "Alle" enthält (alle Tage in der Woche), leicht verstehen kann oder wenn es eher unwichtig ist (alle Kategorien bei Amazon durchsuchen).


0

Ich mag die Idee, explizit eine ALL-Aufzählung zu tragen, aber ich würde sie lieber als Flags setzen

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

Wenn Sie eine Bibliothek wie flagon verwenden oder manuell mit bitweisen Operationen spielen, wenn alle Werte ausgewählt sind, verwenden Sie stattdessen ALL.

Beachten Sie, dass die Werte der Aufzählung immer doppelt so hoch sein sollten wie die des Vorgängers. Und dass Sie zur Überprüfung der Gesundheit niemals eine Aufzählung entfernen sollten, obwohl Sie sie möglicherweise deaktivieren und Ihren Code so anpassen, dass die deaktivierten immer als Teil von ALL gezählt werden.

Obwohl ich stattdessen ein NONE-Flag haben würde und den Kunden entscheiden lassen würde, was zu tun ist, wenn NONE verwendet wird, falls sich das Geschäft später ändert.

Wenn diese Implementierung ausgeführt wird, übergeben Sie stattdessen die Summe der ausgewählten Werte, anstatt die Enuns weiterzugeben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.