Ist es besser, NULL oder leere Werte von Funktionen / Methoden zurückzugeben, bei denen der Rückgabewert nicht vorhanden ist?

135

Ich suche hier eine Empfehlung. Ich habe Probleme damit, ob es besser ist, NULL oder einen leeren Wert von einer Methode zurückzugeben, wenn der Rückgabewert nicht vorhanden ist oder nicht bestimmt werden kann.

Nehmen Sie die folgenden zwei Methoden als Beispiele:

string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID)    // finds a Person with a matching personID.

In ReverseString()würde ich eine leere Zeichenfolge zurückgeben, da der Rückgabetyp Zeichenfolge ist, sodass der Aufrufer dies erwartet. Auf diese Weise müsste der Aufrufer auch nicht prüfen, ob eine NULL zurückgegeben wurde.

In FindPerson()scheint die Rückkehr von NULL besser zu passen. Unabhängig davon, ob NULL oder ein leeres Personenobjekt ( new Person()) zurückgegeben wird oder nicht, muss der Aufrufer prüfen, ob das Personenobjekt NULL oder leer ist, bevor er etwas dagegen unternimmt (wie etwa einen Aufruf UpdateName()). Warum also nicht einfach hier NULL zurückgeben und dann muss der Anrufer nur noch auf NULL prüfen.

Hat jemand anderes damit zu kämpfen? Jede Hilfe oder Einsicht wird geschätzt.

PB
quelle
2
Es wird variieren. Sie könnten auch argumentieren, dass die Methode Werte stoppen soll, die von vornherein falsch sind, z. B., dass der Parameter stringToReverse als leer oder null übergeben wird. Wenn Sie diese Prüfung durchgeführt haben (eine Ausnahme zurückgeworfen haben), wird immer etwas anderes als null zurückgegeben.
Aaron McIver
Fowler POEAA - p. 496 Basismuster - Sonderfall (Nullobjekt) Bloch Effective Java, 2. Ausgabe Punkt 43: Leere Arrays oder Sammlungen zurückgeben, keine Nullen
Boris Treukhov
1
@Boris Ich habe deinen Kommentar nicht gesehen. Ich habe gerade Special Case als Antwort gepostet.
Thomas Owens
@ Thomas Ich sehe kein Problem darin :). Die Frage ist sowieso eine Frage des gesunden Menschenverstands.
Boris Treukhov
Ein interessanter Kommentar ist, dass in Oracle-Datenbanken NULL und die leere Zeichenfolge identisch sind
WuHoUnited 18.11.11

Antworten:

77

Stackoverflow hat eine gute Diskussion über genau dieses Thema in diesem Q & A . In der am besten bewerteten Frage stellt Kronoz fest:

Die Rückgabe von null ist normalerweise die beste Idee, wenn Sie angeben möchten, dass keine Daten verfügbar sind.

Ein leeres Objekt impliziert, dass Daten zurückgegeben wurden, während die Rückgabe von null eindeutig anzeigt, dass nichts zurückgegeben wurde.

Wenn Sie versuchen, auf Mitglieder im Objekt zuzugreifen, führt die Rückgabe einer Null außerdem zu einer Null-Ausnahme. Dies kann nützlich sein, um fehlerhaften Code hervorzuheben. Der Versuch, auf ein Mitglied von nothing zuzugreifen, macht keinen Sinn. Der Zugriff auf Mitglieder eines leeren Objekts schlägt nicht fehl, was bedeutet, dass Fehler unentdeckt bleiben können.

Persönlich möchte ich leere Zeichenfolgen für Funktionen zurückgeben, die Zeichenfolgen zurückgeben, um den Umfang der Fehlerbehandlung zu minimieren, die eingerichtet werden muss. Sie müssen jedoch sicherstellen, dass die Gruppe, mit der Sie arbeiten, der gleichen Konvention folgt. Andernfalls werden die Vorteile dieser Entscheidung nicht erreicht.

Wie auf dem Poster in der SO-Antwort angegeben, sollten jedoch wahrscheinlich Nullen zurückgegeben werden, wenn ein Objekt erwartet wird, damit kein Zweifel darüber besteht, ob Daten zurückgegeben werden.

Am Ende gibt es keinen einzigen Weg, Dinge am besten zu machen. Die Bildung eines Teamkonsenses wird letztendlich die Best Practices Ihres Teams fördern.

JW8
quelle
8
Persönlich möchte ich leere Zeichenfolgen für Funktionen zurückgeben, die Zeichenfolgen zurückgeben, um den Umfang der Fehlerbehandlung zu minimieren, die eingerichtet werden muss. Ich habe dieses Argument nie verstanden. Die Nullprüfung ist was, höchstens <10 Zeichen? Warum verhalten wir uns so?
Aaron McIver
19
Niemand sagte, es sei Rückenarbeit. Aber es ist Arbeit. Es fügt dem Code einen Sonderfall hinzu, der einen weiteren zu testenden Zweig bedeutet. Außerdem wird der Code-Fluss gestört. for x in list_of_things() {...}ist wohl schneller zu grocken alsl = list_of_things(); if l != null {...}
Bryan Oakley
5
@Bryan: Es gibt einen großen Unterschied zwischen einer leeren Werteliste und einer leeren Zeichenfolge. Ein String repräsentiert selten eine Liste von Zeichen.
Kevin Cline
3
@ Kevin Cline: natürlich. Im Fall einer Zeichenfolge möchten Sie jedoch möglicherweise, dass diese Zeichenfolge in einem Protokoll angezeigt wird, unabhängig davon, ob sie null ist oder nicht. Wenn Sie prüfen müssen, ob der Wert null ist, damit Sie eine leere Zeichenfolge drucken können, wird die tatsächliche Absicht verschleiert. Oder es ist so etwas wie ein Datenbankfeld, das vom Benutzer bereitgestellten Inhalt enthält, den Sie einer Webseite hinzufügen möchten. Es ist einfacher emit(user.hometown)alsif user.hometown == null { emit("") else {emit(user.hometown)}
Bryan Oakley
1
Dies ist nicht nur eine zusätzliche Eingabe (keine große Sache), sondern beeinträchtigt auch die Lesbarkeit. Ich persönlich finde Code mit ein paar Null-Checks ablenkend.
ToddR
82

In all dem Code, den ich schreibe, vermeide ich, nullvon einer Funktion zurückzukehren. Ich habe das in Clean Code gelesen .

Das Problem bei der Verwendung nullist, dass die Person, die die Schnittstelle verwendet, nicht weiß, ob nullein mögliches Ergebnis vorliegt und ob sie danach suchen muss, da es keinen not nullReferenztyp gibt.

In F # können Sie einen optionTyp zurückgeben, der some(Person)oder sein kann none, sodass es für den Aufrufer offensichtlich ist, dass er dies überprüfen muss.

Das analoge C # (Anti-) Muster ist die Try...Methode:

public bool TryFindPerson(int personId, out Person result);

Jetzt weiß ich, dass die Leute gesagt haben, sie hassen das Try...Muster, weil ein Ausgabeparameter die Ideen einer reinen Funktion bricht, aber es ist wirklich nicht anders als:

class FindResult<T>
{
   public FindResult(bool found, T result)
   {
       this.Found = found;
       this.Result = result;
   }

   public bool Found { get; private set; }
   // Only valid if Found is true
   public T Result { get; private set;
}

public FindResult<Person> FindPerson(int personId);

... und um ehrlich zu sein, können Sie davon ausgehen, dass jeder .NET-Programmierer über das Try...Muster Bescheid weiß , da es intern vom .NET-Framework verwendet wird. Das bedeutet, dass sie die Dokumentation nicht lesen müssen, um zu verstehen, was für mich wichtiger ist, als an der puristischen Sichtweise der Funktionen festzuhalten (zu verstehen, dass resultes sich um einen outParameter handelt, nicht um einen refParameter).

Ich würde also mitmachen, TryFindPersonweil Sie anscheinend angeben, dass es völlig normal ist, es nicht zu finden.

Wenn es andererseits keinen logischen Grund dafür gibt, dass der Anrufer jemals ein personIdnicht existierendes bereitstellt , würde ich wahrscheinlich Folgendes tun:

public Person GetPerson(int personId);

... und dann würde ich eine Ausnahme auslösen, wenn sie ungültig wäre. Das Get...Präfix impliziert, dass der Anrufer weiß, dass es erfolgreich sein sollte.

Scott Whitlock
quelle
28
+1, ich würde diese Frage mit einem Verweis auf Clean Code beantworten, wenn Sie dies nicht bereits getan hätten. Ich mag das Mantra: "WENN IHRE FUNKTION NICHT TUN KANN, WAS DER NAME SAGT, WIRFEN SIE EINE AUSNAHME." Muuuch besser als alle Dutzend Zeilen oder so auf Null zu prüfen ....
Graham
13
Ich habe es aufgegeben, etwas über das Wissen der Menschen anzunehmen.
CaffGeek
5
"Das Problem bei der Verwendung von null ist, dass die Person, die die Schnittstelle verwendet, nicht weiß, ob null ein mögliches Ergebnis ist und ob sie danach suchen muss, da es keinen Referenztyp gibt, der nicht null ist." Da Referenztypen null sein können, können Sie nicht immer davon ausgehen, dass null ein mögliches Ergebnis ist?
Tommy Carlier
4
Wenn eine Funktion einen Zeiger (C / C ++) oder einen Verweis auf ein Objekt (Java) zurückgibt, gehe ich immer davon aus, dass sie möglicherweise null zurückgibt.
Giorgio
7
"Das Problem bei der Verwendung von null ist, dass die Person, die die Schnittstelle verwendet, nicht weiß, ob null ein mögliches Ergebnis ist und ob sie darauf prüfen muss. [...]" Wenn eine Methode null zurückgeben könnte, sollte dies explizit erfolgen im Rahmen des API-Vertrags erwähnt.
Zsolt Török
28

Sie können Martin Fowlers Sonderfallmuster aus Paterns of Enterprise Application Architecture ausprobieren :

Nullen sind unangenehme Dinge in objektorientierten Programmen, weil sie den Polymorphismus besiegen. Normalerweise können Sie foo für eine Variablenreferenz eines bestimmten Typs frei aufrufen, ohne sich Gedanken darüber zu machen, ob es sich bei dem Element um den genauen Typ oder eine Unterklasse handelt. Mit einer stark typisierten Sprache können Sie sogar den Compiler überprüfen lassen, ob der Aufruf korrekt ist. Da eine Variable jedoch null enthalten kann, kann ein Laufzeitfehler auftreten, wenn Sie eine Meldung auf null aufrufen, die Ihnen einen netten, benutzerfreundlichen Stack-Trace liefert.

Wenn eine Variable Null sein kann, müssen Sie daran denken, sie mit Null-Testcode zu umgeben, damit Sie das Richtige tun, wenn eine Null vorhanden ist. Oft ist das Richtige in vielen Zusammenhängen dasselbe, sodass Sie am Ende an vielen Stellen ähnlichen Code schreiben und die Sünde der Code-Vervielfältigung begehen.

Nullen sind ein häufiges Beispiel für solche Probleme und andere tauchen regelmäßig auf. In Zahlensystemen muss man sich mit Unendlichkeit auseinandersetzen, die spezielle Regeln für Dinge wie Addition enthält, die die üblichen Invarianten von reellen Zahlen brechen. Eine meiner frühesten Erfahrungen mit Unternehmenssoftware war mit einem nicht vollständig bekannten Kunden, der als "Insasse" bezeichnet wurde. All dies impliziert eine Änderung des üblichen Verhaltens des Typs.

Anstatt null oder einen ungeraden Wert zurückzugeben, geben Sie einen Sonderfall zurück, der die gleiche Schnittstelle hat wie der Aufrufer erwartet.

Thomas Owens
quelle
Ich bin auf dieses Szenario gestoßen, aber erst, nachdem die Software veröffentlicht wurde. Meine Testdaten haben es nie ausgelöst, daher war es ein bisschen unvorbereitet und schmerzhaft zu debuggen. Obwohl ich es nicht mit dem Sonderfallmuster gelöst habe, ist es gut, sich dessen bewusst zu sein.
Samis
14

Ich würde denken, ReverseString()würde die umgekehrte Zeichenfolge zurückgeben und es würde ein werfen, IllegalArgumentExceptionwenn in a übergeben Null.

Ich denke, Sie FindPerson()sollten dem NullObject-Muster folgen oder eine ungeprüfte Ausnahme auslösen , wenn Sie etwas nicht finden, wenn Sie immer in der Lage sein sollten, etwas zu finden.

Sich damit auseinandersetzen zu Nullmüssen, sollte vermieden werden. Nachdem Nullin Sprachen wurde ein gerufener Milliarden - Dollar - Fehler von ihm Erfinder ist!

Ich nenne es meinen Milliarden-Dollar-Fehler. Es war die Erfindung der Nullreferenz im Jahr 1965. Damals entwarf ich das erste umfassende Typensystem für Referenzen in einer objektorientierten Sprache (ALGOL W).

Mein Ziel war es, sicherzustellen, dass jede Verwendung von Referenzen absolut sicher ist, wobei die Überprüfung automatisch vom Compiler durchgeführt wird. Aber ich konnte der Versuchung nicht widerstehen, eine Nullreferenz einzufügen, einfach weil es so einfach zu implementieren war.

Dies hat zu unzähligen Fehlern, Sicherheitslücken und Systemabstürzen geführt, die in den letzten vierzig Jahren wahrscheinlich eine Milliarde Dollar an Schmerzen und Schäden verursacht haben.

In den letzten Jahren wurde eine Reihe von Programmanalysatoren wie PREfix und PREfast in Microsoft verwendet, um Verweise zu überprüfen und Warnungen zu geben, wenn das Risiko besteht, dass sie nicht null sind. Neuere Programmiersprachen wie Spec # haben Deklarationen für Nicht-Null-Referenzen eingeführt. Dies ist die Lösung, die ich 1965 abgelehnt habe.

Tony Hoare


quelle
11

Eine Rück Option . Alle Vorteile der Rückgabe eines anderen ungültigen Werts (z. B. das Vorhandensein leerer Werte in Ihren Sammlungen) ohne das Risiko einer NullPointerException.

hugomg
quelle
1
Interessant. Wie implementiere ich eine Option in Java? Und in C ++?
Giorgio
1
@ Giorgio, Für Java haben die Guava-Bibliotheken von Google eine Klasse namens Optional .
Otavio Macedo
1
Für C ++ gibt esboost::optional<T>
MSalters
8

Ich sehe beide Seiten dieses Arguments und stelle fest, dass einige ziemlich einflussreiche Stimmen (z. B. Fowler) befürworten, keine Nullen zurückzugeben, um den Code sauber zu halten, zusätzliche Fehlerbehandlungsblöcke zu vermeiden usw.

Ich stehe jedoch eher auf der Seite der Befürworter einer Nullrückgabe. Ich finde, es gibt einen wichtigen Unterschied beim Aufrufen einer Methode, und sie antwortet, wenn ich keine Daten habe, und sie antwortet, wenn ich diese leere Zeichenfolge habe .

Stellen Sie sich das Szenario vor, in dem Sie versuchen, eine Instanz der Klasse nachzuschlagen, da in einigen Abschnitten auf eine Person-Klasse verwiesen wurde. Wenn Sie ein Finder-Attribut (z. B. eine ID) übergeben, kann ein Client sofort nach null suchen, um festzustellen, ob kein Wert gefunden wurde. Dies ist nicht unbedingt außergewöhnlich (daher sind keine Ausnahmen erforderlich), es sollte jedoch auch klar dokumentiert werden. Ja, das erfordert eine gewisse Sorgfalt seitens des Kunden, und nein, ich denke nicht, dass das überhaupt eine schlechte Sache ist.

Betrachten Sie nun die Alternative, bei der Sie ein gültiges Personenobjekt zurückgeben, das nichts enthält. Tragen Sie in alle Werte (Name, Adresse, favoriteDrink) Nullen ein, oder füllen Sie diese jetzt mit gültigen, aber leeren Objekten auf? Wie soll Ihr Kunde nun feststellen, dass keine tatsächliche Person gefunden wurde? Müssen sie überprüfen, ob der Name eine leere Zeichenfolge anstelle von null ist? Wird so etwas nicht tatsächlich zu so viel oder noch mehr Code-Unordnung und bedingten Anweisungen führen, als wenn wir nur auf Null geprüft und weitermachen würden?

Auch hier gibt es Punkte auf beiden Seiten dieses Arguments, denen ich zustimmen könnte, aber ich finde, dass dies für die meisten Leute am sinnvollsten ist (um den Code wartungsfreundlicher zu machen).

RonU
quelle
Der Unterschied ist, „ist FindPersonoder GetPersoneine Null zurückgeben oder eine Ausnahme auslösen , wenn der Schlüssel nicht existiert?“ Als Benutzer der API weiß ich es einfach nicht und muss die Dokumentation überprüfen. Auf der anderen Seite bool TryGetPerson(Guid key, out Person person)muss ich die Dokumentation nicht überprüfen, und ich weiß, dass keine Ausnahme ausgelöst wird, die einem eher nicht außergewöhnlichen Umstand entspricht. Das ist der Punkt, den ich in meiner Antwort ansprechen möchte.
Scott Whitlock
4
Ich denke, die Leute hingen zu sehr an Ausnahmen. Ausnahmen sind schlecht und ressourcenintensiv. Ganz zu schweigen davon, dass die Leute versuchen, Aussagen zu fangen, ohne etwas zu lösen. Ausnahmen sind ein klares Zeichen dafür, dass etwas grundlegend schief gelaufen ist. Beispielsweise lösen Nullverweise eine Ausnahme aus, da es eindeutig falsch ist, auf Nullobjekte zu verweisen. Es ist falsch, damit zu überprüfen, ob eine Person gefunden wurde oder nicht.
Vladimir Kocjancic
1
@VladimirKocjancic: Ich stimme voll und ganz zu: Ausnahmen sollten für Ausnahmesituationen wie Softwarefehler, Hardwarefehler und Systemfehlkonfigurationen verwendet werden. Wenn Sie eine Teilfunktion (wie findPerson) berechnen , ist es völlig normal, dass Sie kein Ergebnis erhalten. Dies sollte nicht zu einer Ausnahme führen.
Giorgio
6

Geben Sie null zurück, wenn Sie wissen möchten, ob das Element vorhanden ist oder nicht. Andernfalls geben Sie den erwarteten Datentyp zurück. Dies gilt insbesondere dann, wenn Sie eine Liste von Artikeln zurücksenden. Normalerweise kann man davon ausgehen, dass der Anrufer, der eine Liste wünscht, die Liste durchlaufen möchte. Viele (die meisten? Alle?) Sprachen schlagen fehl, wenn Sie versuchen, über null zu iterieren, aber nicht, wenn Sie über eine leere Liste iterieren.

Ich finde es frustrierend, Code wie diesen zu verwenden, nur damit er nicht funktioniert:

for thing in get_list_of_things() {
    do_something_clever(thing)
}

Ich sollte keinen speziellen Fall für den Fall hinzufügen müssen, wenn es keine Dinge gibt .

Bryan Oakley
quelle
7
In Fällen, in denen eine Liste oder eine andere Gruppe von Werten erwartet wird, entscheide ich mich aus genau diesem Grund immer, eine leere Liste zurückzugeben. Die Standardaktion (Durchlaufen einer leeren Liste) ist fast immer richtig. Ist dies nicht der Fall, kann der Anrufer explizit prüfen, ob die Liste leer ist.
TMN
5

Dies hängt von der Semantik der Methode ab. Sie können möglicherweise angeben, dass Ihre Methode nur "Nicht null" akzeptiert. In Java können Sie dies mit Hilfe einiger Metadaten-Annotationen deklarieren:

string ReverseString(@NotNull String stringToReverse)

Sie sollten den Rückgabewert irgendwie angeben. Wenn Sie deklarieren, dass Sie nur NotNull-Werte akzeptieren, müssen Sie eine leere Zeichenfolge zurückgeben (wenn die Eingabe auch eine leere Zeichenfolge war).

Der zweite Fall ist in seiner Semantik etwas kompliziert. Wenn bei dieser Methode eine Person anhand ihres Primärschlüssels zurückgegeben wird (und erwartet wird, dass die Person existiert), ist es besser, eine Ausnahme auszulösen.

Person FindPerson(int personID)

Wenn Sie eine Person anhand einer erratenen ID suchen (was mir seltsam vorkommt), deklarieren Sie diese Methode besser als @Nullable.

Martin Vejmelka
quelle
3

Ich würde empfehlen, wann immer möglich das Null-Objektmuster zu verwenden. Es vereinfacht den Code, wenn Sie Ihre Methoden aufrufen, und Sie können keinen hässlichen Code wie lesen

if (someObject != null && someObject.someMethod () != whateverValue)

Wenn eine Methode beispielsweise eine Auflistung von Objekten zurückgibt, die einem Muster entsprechen, ist die Rückgabe einer leeren Auflistung sinnvoller als die Rückgabe null, und das Durchlaufen dieser leeren Auflistung hat praktisch keine Leistungseinbußen. Ein anderer Fall wäre eine Methode, die die zum Protokollieren von Daten verwendete Klasseninstanz zurückgibt und ein Null-Objekt zurückgibt, anstatt nullmeiner Meinung nach vorzuziehen, da Benutzer nicht gezwungen werden, immer zu überprüfen, ob die zurückgegebene Referenz ist null.

In Fällen, in denen eine Rückgabe nullsinnvoll ist ( findPerson ()z. B. Aufrufen der Methode), würde ich versuchen, mindestens eine Methode bereitzustellen, die zurückgibt, wenn das Objekt vorhanden ist ( personExists (int personId)z. B.), ein anderes Beispiel wäre die containsKey ()Methode auf a Mapin Java. Dadurch wird der Code des Anrufers übersichtlicher, da Sie leicht erkennen können, dass das gewünschte Objekt möglicherweise nicht verfügbar ist (Person existiert nicht, Schlüssel ist nicht in der Karte vorhanden). Die ständige Überprüfung, ob eine Referenz nullverschleiert ist, verschleiert meiner Meinung nach den Code.

Laf
quelle
3

null ist die beste Sache, wenn und nur wenn die folgenden Bedingungen zutreffen:

  • Das Null-Ergebnis wird im normalen Betrieb erwartet . Es ist zu erwarten, dass Sie unter bestimmten Umständen keine Person finden können. FindPerson () gibt null zurück, ist also in Ordnung. Wenn es sich jedoch um einen wirklich unerwarteten Fehler handelt (z. B. in einer Funktion namens saveMyImportantData ()), sollten Sie eine Ausnahme auslösen. Es gibt einen Hinweis im Namen - Ausnahmen gelten für außergewöhnliche Umstände!
  • null bedeutet "nicht gefunden / kein Wert" . Wenn du etwas anderes meinst, dann gib etwas anderes zurück! Gute Beispiele wären Gleitkommaoperationen, die Infinity oder NaN zurückgeben. Dies sind Werte mit bestimmten Bedeutungen, sodass es sehr verwirrend wird, wenn Sie hier null zurückgeben.
  • Die Funktion soll einen einzelnen Wert zurückgeben , z. B. findPerson (). Wenn eine Sammlung zurückgegeben werden soll, z. B. findAllOldPeople (), ist eine leere Sammlung am besten. Eine Folge davon ist, dass eine Funktion, die eine Sammlung zurückgibt, niemals null zurückgeben sollte .

Stellen Sie außerdem sicher, dass Sie die Tatsache dokumentieren, dass die Funktion null zurückgeben kann.

Wenn Sie diese Regeln befolgen, sind Nullen meist harmlos. Beachten Sie, dass Sie normalerweise sofort eine NullPointerException erhalten, wenn Sie vergessen, auf Null zu prüfen. Dies ist normalerweise ein recht einfach zu behebender Fehler. Dieser Fail-Fast-Ansatz ist viel besser als ein gefälschter Rückgabewert (z. B. eine leere Zeichenfolge), der sich im Hintergrund auf Ihrem System ausbreitet und möglicherweise Daten beschädigt, ohne eine Ausnahme auszulösen.

Wenn Sie diese Regeln schließlich auf die beiden in der Frage aufgeführten Funktionen anwenden, gilt Folgendes:

  • FindPerson - Es ist angemessen, dass diese Funktion null zurückgibt, wenn die Person nicht gefunden wurde
  • ReverseString - scheint, als würde es niemals fehlschlagen, ein Ergebnis zu finden, wenn eine Zeichenfolge übergeben wird (da alle Zeichenfolgen, einschließlich der leeren Zeichenfolge, umgekehrt werden können). Es sollte also niemals null zurückgeben. Wenn etwas schief geht (zu wenig Speicher?), Sollte es eine Ausnahme auslösen.
mikera
quelle
1

Meiner Meinung nach gibt es einen Unterschied zwischen der Rückgabe von NULL, der Rückgabe eines leeren Ergebnisses (z. B. der leeren Zeichenfolge oder einer leeren Liste) und dem Auslösen einer Ausnahme.

Normalerweise gehe ich folgendermaßen vor. Ich betrachte eine Funktion oder Methode f (v1, ..., vn) als die Anwendung einer Funktion

f : S x T1 x ... x Tn -> T

Dabei ist S der "Zustand der Welt" T1, ..., Tn die Art der Eingabeparameter und T der Rückgabetyp.

Ich versuche zuerst, diese Funktion zu definieren. Wenn die Funktion partiell ist (dh es gibt einige Eingabewerte, für die sie nicht definiert ist), gebe ich NULL zurück, um dies zu signalisieren. Dies liegt daran, dass die Berechnung normal beendet werden soll und ich weiß, dass die von mir angeforderte Funktion für die angegebenen Eingaben nicht definiert ist. Die Verwendung einer leeren Zeichenfolge als Rückgabewert ist beispielsweise nicht eindeutig, da die Funktion möglicherweise an den Eingängen definiert ist und die leere Zeichenfolge das richtige Ergebnis darstellt.

Ich denke, die zusätzliche Prüfung für einen NULL-Zeiger im aufrufenden Code ist notwendig, weil Sie eine Teilfunktion anwenden und es die Aufgabe der aufgerufenen Methode ist, Ihnen mitzuteilen, ob die Funktion für die angegebene Eingabe nicht definiert ist.

Ich bevorzuge Ausnahmen für Fehler, bei denen die Berechnung nicht möglich ist (dh es konnte keine Antwort gefunden werden).

Angenommen, ich habe eine Klasse Customer und möchte eine Methode implementieren

Customer findCustomer(String customerCode)

in der Anwendungsdatenbank nach einem Kunden anhand seines Codes suchen. Bei dieser Methode würde ich

  • Rückgabe eines Objekts der Klasse Customer bei erfolgreicher Abfrage,
  • Geben Sie null zurück, wenn die Abfrage keinen Kunden findet.
  • Eine Ausnahme auslösen, wenn keine Verbindung zur Datenbank hergestellt werden kann.

Das Extra prüft auf null, z

Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
    ...
}

sind Teil der Semantik dessen, was ich tue, und ich würde sie nicht einfach "überspringen", um den Code besser lesbar zu machen. Ich halte es nicht für eine gute Praxis, die Semantik des vorliegenden Problems zu vereinfachen, nur um den Code zu vereinfachen.

Natürlich ist es gut, wenn die Sprache eine spezielle Syntax unterstützt, da die Überprüfung auf Null sehr häufig auftritt.

Ich würde auch erwägen, das Nullobjektmuster (wie von Laf vorgeschlagen) zu verwenden, solange ich das Nullobjekt einer Klasse von allen anderen Objekten unterscheiden kann.

Giorgio
quelle
0

Methoden, die Auflistungen zurückgeben, sollten leer zurückgeben, andere könnten jedoch null zurückgeben, da es bei den Auflistungen vorkommen kann, dass Sie ein Objekt haben, das jedoch keine Elemente enthält, sodass der Aufrufer nur die Größe und nicht beide überprüft.

Phani
quelle
0

Für mich gibt es hier zwei Fälle. Wenn Sie eine Liste zurücksenden, sollten Sie die Liste immer zurücksenden, egal was passiert. Leer, wenn Sie nichts zu tun haben.

Der einzige Fall, in dem ich eine Debatte sehe, ist, wenn Sie einen einzelnen Artikel zurücksenden. Ich bevorzuge es, im Falle eines Fehlers in einer solchen Situation null zurückzugeben, weil die Dinge schnell scheitern.

Wenn Sie ein Nullobjekt zurückgeben und der Aufrufer ein reales Objekt benötigt, versucht er möglicherweise, das Nullobjekt zu verwenden, was zu unerwartetem Verhalten führt. Ich denke, es ist besser für die Routine, einen Boom zu erleben, wenn sie vergessen, mit der Situation umzugehen. Wenn etwas nicht stimmt, möchte ich so schnell wie möglich eine Ausnahme. Es ist weniger wahrscheinlich, dass Sie einen Bug auf diese Weise versenden.

Loren Pechtel
quelle
0

Hinzufügen zu dem, was bereits über den Typkonstruktor gesagt wurde Maybe:

Ein weiterer großer Vorteil, abgesehen davon, dass man keine NPEs riskiert, ist der semantische. In der funktionalen Welt, in der wir uns mit reinen Funktionen befassen, möchten wir versuchen, Funktionen zu erstellen, deren Anwendung genau ihrem Rückgabewert entspricht . Betrachten Sie den Fall von String.reverse(): Dies ist eine Funktion, bei der eine bestimmte Zeichenfolge die umgekehrte Version dieser Zeichenfolge darstellt. Wir wissen, dass diese Zeichenfolge vorhanden ist, da jede Zeichenfolge umgekehrt werden kann (Zeichenfolgen sind im Grunde geordnete Zeichensätze).

Was ist nun mit findCustomer(int id)? Diese Funktion repräsentiert den Kunden, der die angegebene ID hat. Dies ist etwas, was existieren kann oder nicht. Denken Sie jedoch daran, dass Funktionen nur einen Rückgabewert haben. Sie können also nicht wirklich sagen, dass diese Funktion einen Kunden mit der angegebenen ID zurückgibt ODER dass sie zurückgibt null.

Dies ist mein Hauptproblem sowohl bei der Rückgabe nullals auch bei der Rückgabe eines Null-Objekts. Sie sind irreführend. nullist kein Kunde und es ist auch nicht wirklich "die Abwesenheit eines Kunden". Es ist die Abwesenheit von etwas. Es ist nur ein typloser Hinweis auf NICHTS. Sehr leise und sehr hässlich und sehr fehleranfällig. Ein Null-Objekt ist auch hier meiner Meinung nach ziemlich irreführend. Ein Null-Kunde IST ein Kunde. Es ist nicht die Abwesenheit eines Kunden, es ist nicht der Kunde mit dem gewünschten Ausweis. Dies ist NICHT der Kunde, nach dem ich gefragt habe, aber Sie behaupten immer noch, Sie hätten einen Kunden zurückgegeben. Es hat die falsche ID, das ist eindeutig ein Fehler. Es bricht den durch den Methodennamen und die Unterschrift vorgeschlagenen Vertrag. Der Client-Code muss Informationen zu Ihrem Design enthalten oder die Dokumentation lesen.

Beide Probleme werden durch a gelöst Maybe Customer. Plötzlich sagen wir nicht "Diese Funktion repräsentiert den Kunden mit der angegebenen ID". Wir sagen: "Diese Funktion repräsentiert den Kunden mit der angegebenen ID, wenn sie existiert . Es ist jetzt sehr klar und deutlich, was sie tut. Wenn Sie nach dem Kunden mit einer nicht vorhandenen ID fragen, erhalten Sie Nothing. Wie ist das besser?" als null? nicht nur für die NPE Risiko. Aber auch , weil der Typ Nothingist nicht nur Maybe. Es ist Maybe Customer. Es semantische Bedeutung hat. Es „das Fehlen eines Kunden“ bedeutet insbesondere, was darauf hinweist , dass ein solcher Kunde nicht existiert.

Ein weiteres Problem mit null ist natürlich, dass es mehrdeutig ist. Ist es so, dass Sie den Kunden nicht gefunden haben, oder gab es einen DB-Verbindungsfehler oder darf ich diesen Kunden einfach nicht sehen?

Wenn Sie mehrere Fehlerfälle wie diesen behandeln müssen, können Sie entweder Ausnahmen für diese Versionen auslösen oder (und ich bevorzuge dies) eine zurückgeben Either CustomerLoadError Customer, die entweder einen Fehler oder den zurückgegebenen Kunden darstellt. Es bietet alle Vorteile Maybe Customerund ermöglicht es Ihnen, anzugeben, was schief gehen kann.

Das Wichtigste, wonach ich strebe, ist, den Vertrag der Funktion in ihrer Unterschrift festzuhalten. Gehen Sie nicht von Dingen aus, verlassen Sie sich nicht auf flüchtige Konventionen, seien Sie explizit.

Sara
quelle
0

1) Wenn die Semantik der Funktion ist, dass sie nichts zurückgeben kann, MUSS der Aufrufer sie testen, sonst wird er z. Nimm dein Geld und gib es niemandem.

2) Es ist gut, Funktionen zu erstellen, die semantisch immer etwas zurückgeben (oder werfen).

Bei einem Logger ist es sinnvoll, einen Logger zurückzugeben, der nicht protokolliert, wenn kein Logger definiert wurde. Bei der Rückgabe von Auflistungen ist es fast nie sinnvoll (außer auf "niedrig genug", wo die Auflistung selbst DIE Daten ist, nicht das, was sie enthält), nichts zurückzugeben, da eine leere Menge eine Menge ist, nicht nichts.

Mit einem Personenbeispiel würde ich den Weg des "Ruhezustands" gehen (Get vs Load, IIRC, aber dort wird es durch Proxy-Objekte zum verzögerten Laden kompliziert), zwei Funktionen zu haben, eine, die null zurückgibt, und eine, die wirft.

user470365
quelle
-1
  • NULL sollte zurückgegeben werden, wenn die Anwendung erwartet, dass Daten verfügbar sind, diese jedoch nicht verfügbar sind. Beispielsweise sollte ein Dienst, der die CITY basierend auf der Postleitzahl zurückgibt, null zurückgeben, wenn der Ort nicht gefunden wird. Der Anrufer kann dann entscheiden, ob er mit der Null umgehen oder in die Luft jagen möchte.

  • Eine leere Liste sollte zurückgegeben werden, wenn zwei Möglichkeiten bestehen. Daten verfügbar oder KEINE Daten verfügbar. Zum Beispiel ein Dienst, der die STADT (en) zurückgibt, wenn die Population eine bestimmte Anzahl überschreitet. Es kann eine leere Liste zurückgegeben werden, wenn keine Daten vorhanden sind, die den angegebenen Kriterien entsprechen.

java_mouse
quelle
-2

Returning NULL ist ein schreckliches Design in einer objektorientierten Welt. Kurz gesagt, die Verwendung von NULL führt zu:

  • Ad-hoc-Fehlerbehandlung (anstelle von Ausnahmen)
  • mehrdeutige Semantik
  • langsames statt schnelles Versagen
  • Computerdenken vs. Objektdenken
  • veränderliche und unvollständige Objekte

In diesem Blog-Beitrag finden Sie eine ausführliche Erklärung: http://www.yegor256.com/2014/05/13/why-null-is-bad.html

yegor256
quelle