Sollte ich bei einem aussagekräftigen Wert außerhalb des Bereichs eine Ausnahme auslösen oder selbst damit umgehen?

42

Ich habe eine Struktur geschrieben, die Breiten- / Längengradkoordinaten darstellt. Ihre Werte reichen von -180 bis 180 für Längen und 90 bis -90 für Breiten.

Wenn mir ein Benutzer dieser Struktur einen Wert außerhalb dieses Bereichs gibt, habe ich zwei Möglichkeiten:

  1. Ausnahme auslösen (Argument außerhalb des gültigen Bereichs)
  2. Konvertieren Sie den Wert in die Einschränkung

Da eine Koordinate von -185 eine Bedeutung hat (sie kann sehr leicht in +175 konvertiert werden, da dies Polarkoordinaten sind), könnte ich sie akzeptieren und konvertieren.

Ist es besser, eine Ausnahme auszulösen, um dem Benutzer mitzuteilen, dass mir sein Code einen Wert gegeben hat, den er nicht haben sollte?

Edit: Auch ich kenne den Unterschied zwischen lat / lng und Koordinaten, aber ich wollte das vereinfachen, um die Diskussion zu vereinfachen - es war nicht die hellste Idee

K. Gkinis
quelle
13
Soll der Benutzer einen Wert außerhalb des Bereichs eingeben dürfen? Wenn die Antwort nein ist, werfen Sie eine Ausnahme. Wenn die Regeln nicht so streng sind, führen Sie die Konvertierung durch, geben Sie jedoch in der Dokumentation ausdrücklich an, dass die Konvertierung möglicherweise erfolgt. Beachten Sie auch, dass die Ausnahmebehandlung in einigen Sprachen sehr kostspielig ist.
Andy
C #, spielt es eine Rolle? Einschränkungen werden von Haus aus nicht unterstützt, wenn Sie dies meinen.
K. Gkinis
2
Es scheint mir ziemlich klar zu sein, dass die richtige Vorgehensweise es dem Benutzer erlaubt, irgendetwas außerhalb des Bereichs einzugeben und dabei eine Ausnahme auszulösen (wenn der Standard besagt, dass der abs-Wert nicht über 180 liegen darf und einen höheren Wert als diesen angibt) eine klare Verletzung). Übrigens ist C # eine der Sprachen, in denen Ausnahmen recht kostspielig sind. Verwenden Sie sie daher nur in Ausnahmesituationen. Wenn Sie sie nicht erkennen, kann dies zu Problemen in Ihrer Anwendung führen.
Andy
2
Ich neige dazu, mich davon abzuhalten, Annahmen darüber zu machen, was der Benutzer durch die Übergabe bestimmter Parameterwerte, insbesondere derjenigen, die mein Code nicht berücksichtigt, "meinte". Klingt nach einem ähnlichen Fall.
18.
2
Die Web Mercator-Koordinaten liegen nicht zwischen -180 und 180 und -90 und 90. Das ist der Breiten- / Längengrad (und dafür gibt es sogar mehrere Koordinatensysteme). Mercator-Projektionen liegen typischerweise im Bereich von Hunderttausenden oder Millionen und haben Einheiten von "Metern" (nicht einmal streng genommen, da die Länge jeder Einheit eine zunehmende reale Bodenentfernung abdeckt, wenn Sie sich den Polen nähern), nicht Grad. Selbst in Grad ausgedrückt ist es auf ± 85,051129 Grad beschränkt, da die Projektion an den Polen unendlich breit wird. (Ich habe eine Bearbeitung eingereicht, die dies korrigiert, da dies nicht der Kern der Frage ist.)
jpmc26

Antworten:

51

Wenn der Kern Ihrer Frage das ist ...

Wenn ein Client-Code ein Argument übergibt, dessen Wert für das, was meine Datenstruktur modelliert, ungültig ist, sollte ich den Wert ablehnen oder in etwas Sinnvolles konvertieren?

... dann würde meine allgemeine Antwort "ablehnen" lauten, da dies dazu beiträgt, die Aufmerksamkeit auf potenzielle Fehler im Client-Code zu lenken, die dazu führen, dass der ungültige Wert im Programm angezeigt wird und Ihren Konstruktor erreicht. Die Aufmerksamkeit auf Fehler zu lenken, ist in den meisten Systemen eine erwünschte Eigenschaft, zumindest während der Entwicklung (es sei denn, es handelt sich um eine erwünschte Eigenschaft Ihres Systems, die bei Fehlern durcheinandergebracht werden soll).

Die Frage ist, ob Sie diesem Fall tatsächlich gegenüberstehen .

  • Wenn Ihre Datenstruktur im Allgemeinen Polarkoordinaten modellieren soll, akzeptieren Sie den Wert, da Winkel außerhalb des Bereichs -180 und +180 nicht wirklich ungültig sind. Sie sind vollkommen gültig und haben zufällig immer ein Äquivalent im Bereich von -180 bis +180 (und wenn Sie sie in einen Zielbereich umwandeln möchten, fühlen Sie sich frei - der Client-Code muss sich normalerweise nicht darum kümmern). .

  • Wenn Ihre Datenstruktur explizit Web Mercator-Koordinaten modelliert (gemäß der Frage in ihrer ursprünglichen Form), befolgen Sie am besten die in der Spezifikation genannten Bestimmungen (die ich nicht kenne, daher werde ich nichts dazu sagen). . Wenn die Spezifikation des Objekts, das Sie modellieren, besagt, dass einige Werte ungültig sind, lehnen Sie sie ab. Wenn es heißt, dass sie als etwas Sinnvolles interpretiert werden können (und somit tatsächlich gültig sind), akzeptieren Sie sie.

Der Mechanismus, mit dem Sie signalisieren, ob die Werte akzeptiert wurden oder nicht, hängt von den Merkmalen Ihrer Sprache, ihrer allgemeinen Philosophie und Ihren Leistungsanforderungen ab. Sie könnten also eine Ausnahme auslösen (im Konstruktor) oder eine nullfähige Version Ihrer Struktur zurückgeben (über eine statische Methode, die einen privaten Konstruktor aufruft) oder einen Booleschen Wert zurückgeben und Ihre Struktur als outParameter an den Aufrufer übergeben (erneut über ein statische Methode, die einen privaten Konstruktor aufruft) und so weiter.

Theodoros Chatzigiannakis
quelle
12
Ein Längengrad außerhalb des Bereichs von -180 bis +180 sollte wahrscheinlich als akzeptabel angesehen werden, aber ein Breitengrad außerhalb des Bereichs von -90 bis +90 erscheint unsinnig. Sobald man den Nord- oder Südpol erreicht, ist eine weitere Reise nach Norden oder Süden nicht mehr definiert.
Supercat
4
@supercat Ich stimme Ihnen eher zu, aber da ich nicht weiß, welche Werte in der Web Mercator-Spezifikation tatsächlich ungültig sind, versuche ich, keine harten Schlussfolgerungen zu ziehen. Das OP kennt die Problemdomäne besser als ich.
Theodoros Chatzigiannakis
1
@supercat: Start dort, wo der Meridian den Äquator erreicht. Reise entlang des Meridians nach dem Breitengrad. Wenn der Breitengrad> 90 ist, fahren Sie einfach weiter auf dem gleichen großen Kreis. Kein Problem. Dokumentieren Sie es und überlassen Sie den Rest dem Kunden.
Kevin Cline
4
@supercat Es ist schlimmer als das. In der Web Mercator-Projektion werden ± 90 tatsächlich auf eine unendliche Breite projiziert. Der Standard schneidet ihn also tatsächlich bei ± 85.051129 ab. Auch lat / long! = Web-Mercator-Koordinaten. Mercator-Koordinaten verwenden eine Variation von Metern und nicht von Grad. (Wenn Sie sich den Polen nähern, entspricht jeder "Meter" tatsächlich einem immer größeren Stück Boden.) Die OP-Koordinaten sind rein lat / lang. Web Mercator hat nichts mit ihnen zu tun, außer dass sie möglicherweise über einer Web Mercator-Basiskarte angezeigt werden und eine räumliche Bibliothek für sie unter der Haube projiziert wird.
jpmc26
1
Ich sollte "das Äquivalent von ± 85.051129" sagen, da dies nicht die tatsächliche Koordinate ist.
jpmc26
10

Es kommt sehr darauf an. Aber Sie sollten sich entscheiden , etwas zu tun und es zu dokumentieren .

Das einzig absolut Falsche für Ihren Code ist, zu vergessen, dass die Benutzereingabe möglicherweise außerhalb des erwarteten Bereichs liegt, und Code zu schreiben, der versehentlich ein bestimmtes Verhalten aufweist. Denn dann gehen einige Leute falsch davon aus, wie sich Ihr Code verhält, und es kommt zu Fehlern, während andere davon abhängen, wie sich Ihr Code versehentlich verhält (auch wenn dieses Verhalten völlig verrückt ist), und Sie werden mehr Fehler verursachen Wenn Sie das Problem später beheben.

In diesem Fall kann ich Argumente so oder so sehen. Wenn jemand von 175 auf +10 Grad fährt, sollte er bei -175 enden. Wenn Sie immer normalisieren Benutzereingaben und so behandeln 185 als gleichwertig mit -175 dann Client - Code kann nicht die falsche Sache tun , wenn es 10 Grad addiert; es hat immer die richtige wirkung. Wenn Sie 185 als Fehler behandeln, erzwingen Sie jeden Fall, in dem der Client-Code relative Grade hinzufügt, um sie in die Normalisierungslogik einzufügen (oder denken Sie zumindest daran, Ihre Normalisierungsprozedur aufzurufen), was tatsächlich dazu führtBugs (obwohl es hoffentlich leicht ist, solche zu finden, die schnell zerdrückt werden). Wenn jedoch eine Längengradzahl vom Benutzer eingegeben, wörtlich im Programm geschrieben oder durch eine Prozedur berechnet wird, die immer auf [-180, 180) eingestellt ist, weist ein Wert außerhalb dieses Bereichs mit hoher Wahrscheinlichkeit auf einen Fehler hin "das konvertieren könnte probleme verbergen.

Mein Ideal wäre in diesem Fall wahrscheinlich, einen Typ zu definieren, der die richtige Domäne darstellt. Verwenden Sie einen abstrakten Typ (lassen Sie den Client-Code nicht einfach auf die darin enthaltenen unformatierten Zahlen zugreifen) und stellen Sie sowohl eine normalisierende als auch eine validierende Factory bereit (damit der Client den Kompromiss eingehen kann). Unabhängig davon, welcher Wert für diesen Typ festgelegt wurde, sollte 185 in Ihrer öffentlichen API nicht von -175 zu unterscheiden sein (unabhängig davon, ob sie bei der Erstellung konvertiert wurden oder ob Sie Gleichheit, Zugriffsmethoden und andere Operationen bereitstellen, die den Unterschied irgendwie ignorieren). .

Ben
quelle
3

Wenn es Ihnen nicht wirklich wichtig ist, eine Lösung zu wählen, können Sie den Benutzer einfach entscheiden lassen.

Wenn Ihre Struktur ein schreibgeschütztes Wertobjekt ist und von einer Methode / einem Konstruktor erstellt wurde, können Sie basierend auf den Optionen, die der Benutzer hat, zwei Überladungen bereitstellen:

  • Ausnahme auslösen (Argument außerhalb des gültigen Bereichs)
  • Konvertieren Sie den Wert in die Einschränkung

Lassen Sie den Benutzer auch niemals eine ungültige Struktur zu, um sie an Ihre anderen Methoden weiterzugeben, und korrigieren Sie sie bei der Erstellung.

Bearbeiten: basierend auf den Kommentaren gehe ich davon aus, dass Sie C # verwenden.

Filipe Borges
quelle
Danke für die Eingabe! Ich werde darüber nachdenken, obwohl ich befürchte, dass es den Zweck des Konstruktors zum Auslösen von Ausnahmen untergraben würde, wenn man es einfach vermeiden könnte. Es ist aber interessant!
K. Gkinis
Wenn Sie diese Idee verwenden möchten, ist es besser, eine Schnittstelle zu definieren und zwei Implementierungen zu verwenden , die ausgelöst oder konvertiert werden. Es wäre schwierig, einen Konstruktor auf eine vernünftige Weise zu überladen, um so zu arbeiten, wie Sie es beschrieben haben.
2
@Snowman: Ja, es wäre schwierig, einen Konstruktor mit denselben Argumenttypen zu überladen, aber es wäre nicht schwierig, zwei statische Methoden und einen privaten Konstruktor zu haben.
Wchargin
1
@ K.Gkinis: Der Zweck des Konstruktors zum Auslösen von Ausnahmen besteht nicht darin, sicherzustellen, dass die Anwendung stirbt. Schließlich kann der Client immer catchIhre Ausnahmen ausführen . Wie andere gesagt haben, soll es dem Kunden gestattet sein, sich selbst einzuschränken, wenn er dies wünscht. Sie umgehen nichts wirklich.
wchargin
Dieser Vorschlag verkompliziert den Code ohne Nutzen. Entweder sollte die Methode ungültige Eingaben konvertieren oder nicht. Durch zwei Überladungen wird der Code komplexer, ohne das Dilemma zu lösen.
JacquesB
0

Es hängt davon ab, ob die Eingabe direkt von einem Benutzer über eine Benutzeroberfläche oder vom System stammt.

Eingabe über eine Benutzeroberfläche

Es ist eine Frage der Benutzererfahrung, wie mit ungültigen Eingaben umgegangen wird. Ich weiß nichts über Ihren speziellen Fall, aber im Allgemeinen gibt es ein paar Möglichkeiten:

  • Weisen Sie den Benutzer auf den Fehler hin und lassen Sie ihn beheben, bevor Sie fortfahren. (Am häufigsten)
  • Automatisch in den gültigen Bereich konvertieren (falls möglich), den Benutzer jedoch über die Änderung informieren und dem Benutzer die Überprüfung gestatten, bevor er fortfährt.
  • In den gültigen Bereich umwandeln und fortfahren.

Die Auswahl hängt von den Erwartungen Ihrer Benutzer ab und davon, wie kritisch die Daten sind. Zum Beispiel korrigiert Google die Rechtschreibung in Abfragen automatisch, dies ist jedoch ein geringes Risiko, da eine nicht hilfreiche Änderung kein Problem darstellt und leicht zu beheben ist (und selbst dann wird auf der Ergebnisseite klargestellt, dass die Abfrage geändert wurde). Wenn Sie hingegen Koordinaten für eine Nuklearrakete eingeben, möchten Sie möglicherweise eine strengere Eingabevalidierung und keine stillschweigenden Korrekturen ungültiger Daten. Es gibt also keine allgemeingültige Antwort.

Am wichtigsten ist, dass Sie überlegen, ob die Korrektur von Eingaben überhaupt einen Nutzen für den Benutzer hat. Warum sollte ein Benutzer ungültige Daten eingeben? Es ist leicht zu erkennen, wie jemand einen Rechtschreibfehler machen könnte, aber warum sollte jemand einen Längengrad von -185 eingeben? Wenn der Benutzer wirklich +175 gemeint hätte, hätte er wahrscheinlich +175 eingegeben. Ich denke, es ist höchstwahrscheinlich, dass ein ungültiger Längengrad einfach ein Tippfehler ist und der Benutzer -85 oder etwas anderes meinte. In diesem Fall ist eine stille Konvertierung schlecht und nicht hilfreich . Der benutzerfreundlichste Ansatz für Ihre App besteht wahrscheinlich darin, den Benutzer auf den ungültigen Wert aufmerksam zu machen und ihn selbst korrigieren zu lassen.

Eingabe über eine API

Wenn die Eingabe von einem anderen System oder Subsystem stammt, besteht keine Frage. Sie sollten eine Ausnahme auslösen. Sie sollten niemals ungültige Eingaben von einem anderen System stillschweigend konvertieren, da dies zu einer Maskierung von Fehlern an anderer Stelle im System führen kann. Wenn die Eingabe "korrigiert" wird, sollte dies auf der Benutzeroberflächenebene und nicht tiefer im System geschehen.

JacquesB
quelle
0

Sie sollten eine Ausnahme auslösen.

In dem Beispiel, das Sie angegeben haben, 185 gesendet und in -175 konvertiert, kann es in einigen Fällen nützlich sein, diese Funktionalität bereitzustellen. Aber was ist, wenn der Anrufer 1 Million sendet? Wollen sie das wirklich umsetzen? Es scheint eher ein Fehler zu sein. Wenn Sie also eine Ausnahme für 1.000.000, aber nicht für 185 auslösen müssen, müssen Sie eine Entscheidung über einen beliebigen Schwellenwert für das Auslösen einer Ausnahme treffen. Dieser Schwellenwert wird Sie manchmal stören, wenn eine anrufende App Werte um den Schwellenwert sendet.

Es ist besser, die Ausnahme für Werte außerhalb des Bereichs auszulösen.

brendan
quelle
-1

Die bequemste Option für einen Entwickler wäre eine Fehlerunterstützung bei der Kompilierung auf der Plattform für Werte außerhalb des Bereichs. In diesem Fall sollte der Bereich ebenso wie der Typ der Parameter Teil der Methodensignatur sein. So wie Ihr API-Benutzer keinen String übergeben kann, wenn für Ihre Methodensignatur eine Ganzzahl definiert ist , hätte der Benutzer keinen Wert übergeben können, ohne zu prüfen, ob der Wert innerhalb des in der Methodensignatur angegebenen Bereichs liegt. Wenn nicht markiert, sollte er einen Fehler zur Kompilierungszeit bekommen und somit könnte ein Laufzeitfehler vermieden werden.

Derzeit unterstützen jedoch nur sehr wenige Compiler / Plattformen diese Art der Überprüfung der Kompilierzeit. Es ist also ein Entwickler-Kopfschmerz. Im Idealfall sollte Ihre Methode jedoch nur eine sinnvolle Ausnahme für nicht unterstützte Werte auslösen und diese klar dokumentieren.

Übrigens, ich liebe das Fehlermodell, das Joe Duffy hier vorgeschlagen hat .

Gulshan
quelle
Wie würden Sie Kompilierungszeitfehler für Benutzereingaben bereitstellen?
JacquesB
@JacquesB Der Compiler gibt einen Fehler aus, wenn die Benutzereingabe nach dem Einfügen nicht als innerhalb des Bereichs liegend überprüft wird , unabhängig davon, ob der tatsächliche Wert innerhalb des Bereichs liegt.
Gulshan
Dann müssen Sie sich jedoch noch entscheiden, ob Sie die Eingabe ablehnen oder in einen gültigen Bereich konvertieren möchten, damit die ursprüngliche Frage nicht beantwortet wird.
JacquesB
@JacquesB Ich sollte dies sagen. Zuerst dachte ich immer, OP entwickelt eine API und die Benutzeroberfläche wird von jemand anderem entwickelt, der seine API nutzt. Ich habe mich in diesem Punkt geirrt. Ich habe die Frage gerade noch einmal gelesen und realisiert. Ich versuche nur zu sagen, dass die Validierung bei API-Verbrauchern durchgeführt werden sollte und wenn nicht, sollte der Compiler einen Fehler auslösen.
Gulshan
Also ... müssen Sie eine neue Klasse erstellen, die die Eingaben und Überprüfungen enthält. Was passiert, wenn Sie das Klassenobjekt aus den unformatierten Eingaben erstellen? Eine Ausnahme auslösen? Lautlos vorbei? Verschieben Sie einfach das Problem.
Djechlin
-1

Standardmäßig sollte eine Ausnahme ausgelöst werden. Sie können auch eine Option zulassen strict=falseund den Zwang basierend auf der Flagge ausführen, wobei dies natürlich strict=truedie Standardeinstellung ist. Das ist ziemlich häufig:

  • Java DateFormatunterstützt lenient .
  • Gsons JSON-Parser unterstützt auch einen nachsichtigen Modus.
  • Usw.
djechlin
quelle
Dies kompliziert den Code ohne Nutzen.
JacquesB
@JacquesB Ausnahmebedingung standardmäßig auslösen oder zulassen strict=false?
Djechlin
@JacquesB Ich habe einige Beispiele für APIs hinzugefügt, die strenge und milde Modi unterstützen.
Djechlin
-2

Für mich ist es die beste Praxis, Benutzereingaben niemals zu ändern. Der Ansatz, den ich normalerweise verfolge, besteht darin, die Validierung von der Ausführung zu trennen.

  • Haben Sie eine einfache Klasse, die nur die angegebenen Parameter verwendet.
  • Verwenden Sie einen Dekorator, um eine Validierungsebene bereitzustellen, die nach Belieben geändert werden kann, ohne die Ausführungsklasse zu beeinträchtigen (oder injizieren Sie einen Validator, wenn dieser Ansatz zu schwierig ist).
David A
quelle
Der "Benutzer", auf den sich die Frage bezieht, ist der Entwickler, der die API verwendet, nicht der Endbenutzer ...
Jay Elston