wake-up-neo.com

Gibt es ein falsches Design zurück?

Ich habe einige Stimmen gehört, die sagen, dass das Überprüfen auf einen zurückgegebenen Nullwert von Methoden schlechtes Design ist. Ich würde gerne einige Gründe dafür hören.

pseudocode:

variable x = object.method()
if (x is null) do something
116
koen

Der Grund für die Nichtrückgabe von null ist, dass Sie nicht darauf prüfen müssen, und daher muss Ihr Code nicht basierend auf dem Rückgabewert einem anderen Pfad folgen. Vielleicht möchten Sie das Null Object Pattern auschecken, das weitere Informationen dazu enthält.

Wenn ich beispielsweise in Java eine Methode definieren würde, die eine Collection zurückgegeben hat, würde ich normalerweise eher eine leere Collection (d. H. Collections.emptyList()) als null zurückgeben, da dies bedeutet, dass mein Client-Code sauberer ist. z.B.

Collection<? extends Item> c = getItems(); // Will never return null.

for (Item item : c) { // Will not enter the loop if c is empty.
  // Process item.
}

... was sauberer ist als:

Collection<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}
191
Adamski

Hier ist der Grund.

In Clean Code von Robert Martin schreibt er, dass die Rückgabe von null ein schlechtes Design ist, wenn Sie stattdessen beispielsweise leeres Array zurückgeben können. Da das erwartete Ergebnis ein Array ist, warum nicht? Dadurch können Sie das Ergebnis ohne zusätzliche Bedingungen wiederholen. Wenn es eine ganze Zahl ist, reicht vielleicht 0 aus, wenn es ein Hash ist, leerer Hash. usw.

Die Voraussetzung ist, den Aufrufcode nicht zu zwingen, Probleme sofort zu behandeln. Der Anrufcode möchte sich möglicherweise nicht mit ihnen beschäftigen. Das ist auch der Grund, warum Ausnahmen in vielen Fällen besser sind als null.

66
Max Chernyak

Gute Verwendung der Rückgabe von Null:

  • Wenn null ein gültiges funktionales Ergebnis ist, beispielsweise: FindFirstObjectThatNeedsProcessing () kann null zurückgeben, wenn es nicht gefunden wird, und der Aufrufer sollte dies entsprechend prüfen.

Missbrauch: Versuch, Ausnahmesituationen zu ersetzen oder auszublenden, z.

  • catch (...) und gibt null zurück
  • Die API-Abhängigkeitsinitialisierung ist fehlgeschlagen
  • Nicht genügend Speicherplatz
  • Ungültige Eingabeparameter (Programmierfehler, Eingaben müssen vom Aufrufer bereinigt werden)
  • usw

In diesen Fällen ist das Auslösen einer Ausnahme angemessener, da

  • Ein Nullrückgabewert liefert keine aussagekräftigen Fehlerinformationen
  • Der unmittelbare Aufrufer kann die Fehlerbedingung höchstwahrscheinlich nicht verarbeiten
  • Es gibt keine Garantie dafür, dass der Anrufer nach Nullergebnissen sucht

Ausnahmen sollten jedoch nicht für normale Programmbetriebsbedingungen verwendet werden, z.

  • Ungültiger Benutzername/Passwort (oder von Benutzern eingegebene Eingaben)
  • Brechen von Schleifen oder als nicht lokale Gotos
35
ZeroConcept

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

  • ad-hoc-Fehlerbehandlung (anstelle von Ausnahmen)
  • mehrdeutig semantisch
  • langsam statt schnell ausfallen
  • computerdenken statt Objektdenken
  • veränderliche und unvollständige Objekte

In diesem Blogpost finden Sie eine detaillierte Erklärung: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Mehr in meinem Buch Elegante Objekte , Abschnitt 4.1.

26
yegor256

Wer sagt das ist schlechtes Design?

Das Überprüfen auf Nullen ist eine übliche Praxis, die sogar empfohlen wird, da Sie sonst überall das Risiko von NullReferenceExceptions eingehen. Es ist besser, den Fehler elegant zu behandeln, als Ausnahmen zu werfen, wenn dies nicht erforderlich ist.

22
Brandon

Basierend auf dem, was Sie bisher gesagt haben, gibt es meiner Meinung nach nicht genügend Informationen. 

Die Rückgabe von null aus einer CreateWidget () - Methode scheint schlecht zu sein. 

Die Rückgabe von null aus einer FindFooInBar () - Methode scheint in Ordnung zu sein. 

17
jcollum

Sein Erfinder sagt es ist ein Fehler in Milliardenhöhe!

12
Bahadır Yağan

Das hängt von der verwendeten Sprache ab. Wenn Sie sich in einer Sprache wie C # befinden, in der das Fehlen eines Werts idiomatisch Null zurückgeben soll, ist die Rückgabe von Null ein gutes Design, wenn Sie keinen Wert haben. Alternativ wäre in Sprachen wie Haskell, die idiomatisch die Vielleicht-Monade für diesen Fall verwenden, die Rückgabe von NULL ein schlechtes Design (wenn überhaupt möglich).

10
Greg Beech

Wenn Sie alle Antworten lesen, wird deutlich, dass die Antwort auf diese Frage von der Art der Methode abhängt. 

Erstens, wenn etwas Außergewöhnliches passiert (IO-Problem usw.), werden logisch Ausnahmen ausgelöst. Wann genau etwas Außergewöhnliches ist, ist wahrscheinlich etwas für ein anderes Thema. 

Wenn erwartet wird, dass eine Methode möglicherweise keine Ergebnisse liefert, gibt es zwei Kategorien:

  • Wenn es möglich ist, einen neutralen Wert zurückzugeben, tun Sie dies.
    Leere Aufzählungen, Zeichenfolgen usw. sind gute Beispiele
  • Wenn ein solcher neutraler Wert nicht vorhanden ist, sollte null zurückgegeben werden.
    . Wie erwähnt, wird davon ausgegangen, dass die Methode möglicherweise kein Ergebnis hat, daher ist sie nicht außergewöhnlich und sollte daher keine Ausnahme auslösen. Ein neutraler Wert ist nicht möglich (zum Beispiel: 0 ist nicht unbedingt ein neutrales Ergebnis, abhängig vom Programm)

Bis wir einen offiziellen Weg haben, um anzuzeigen, dass eine Funktion null zurückgeben kann oder nicht, versuche ich eine Namenskonvention, um dies zu bezeichnen.
Genau wie Sie dieTry Something () - Konvention für Methoden haben, von denen erwartet wird, dass sie fehlschlagen, nenne ich oft meine MethodenSafe Something () , wenn die Methode zurückkehrt ein neutrales Ergebnis anstelle von null. 

Ich bin mit dem Namen noch nicht ganz in Ordnung, konnte aber nichts Besseres finden. Also renne ich jetzt damit.

5
Boris Callens

Ausnahmen sind für ausnahmealle Umstände.

Wenn Ihre Funktion ein Attribut finden soll, das einem bestimmten Objekt zugeordnet ist, und dieses Objekt über kein solches Attribut verfügt, kann es sinnvoll sein, NULL zurückzugeben. Wenn das Objekt nicht vorhanden ist, ist es möglicherweise sinnvoller, eine Ausnahme auszulösen. Wenn die Funktion eine Liste von Attributen zurückgeben soll und keine zurückgegeben werden soll, ist die Rückgabe einer leeren Liste sinnvoll - Sie geben alle Nullattribute zurück.

3
HyperHacker

Ich habe eine Versammlung in diesem Bereich, die mir gute Dienste geleistet hat

Für Einzelartikelabfragen:

  • Create... gibt eine neue Instanz zurück, oder wirft
  • Get... gibt eine erwartete vorhandene Instanz zurück, oder wirft
  • GetOrCreate... gibt eine vorhandene Instanz zurück oder eine neue Instanz, wenn keine vorhanden ist, oder wirft
  • Find... gibt eine vorhandene Instanz zurück, falls vorhanden, oder null

Für Sammlungsabfragen:

  • Get... gibt immer eine Sammlung zurück, die leer ist, wenn keine übereinstimmenden [1] Elemente gefunden werden

[1] bestimmte Kriterien, explizit oder implizit, angegeben im Funktionsnamen oder als Parameter.

3
Johann Gerell

Es ist in Ordnung, null zurückzugeben, wenn dies auf irgendeine Weise sinnvoll ist:

public String getEmployeeName(int id){ ..}

In einem solchen Fall ist es sinnvoll, den Wert null zurückzugeben, wenn die ID keiner vorhandenen Entität entspricht, da Sie damit den Fall unterscheiden können, in dem keine Übereinstimmung gefunden wurde, von einem legitimen Fehler.

Die Leute mögen das für schlecht halten, weil es als "spezieller" Rückgabewert missbraucht werden kann, der auf einen Fehlerzustand hinweist, der nicht so gut ist, ein bisschen wie die Rückgabe von Fehlercodes von einer Funktion, aber verwirrend, da der Benutzer die Rückgabe auf überprüfen muss null, anstatt die entsprechenden Ausnahmen zu erfassen, z.

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}
3
Steve B.

Es ist nicht unbedingt ein schlechtes Design - wie bei so vielen Designentscheidungen hängt es davon ab.

Wenn das Ergebnis der Methode etwas ist, das bei normaler Verwendung kein gutes Ergebnis hätte, ist die Rückgabe von null in Ordnung:

object x = GetObjectFromCache();   // return null if it's not in the cache

Wenn es wirklich immer ein Nicht-Null-Ergebnis geben sollte, ist es möglicherweise besser, eine Ausnahme auszulösen:

try {
   Controller c = GetController();    // the controller object is central to 
                                      //   the application. If we don't get one, 
                                      //   we're fubar

   // it's likely that it's OK to not have the try/catch since you won't 
   // be able to really handle the problem here
}
catch /* ... */ {
}
3
Michael Burr

Für bestimmte Szenarien möchten Sie einen Fehler so schnell wie möglich feststellen.

Die Überprüfung auf NULL und das Feststellen von Fehlern (für Programmierfehler) oder das Abwerfen (für Benutzer- oder Anruferfehler) im Fehlerfall kann dazu führen, dass spätere Abstürze schwieriger zu finden sind, da der ursprüngliche ungerade Fall nicht gefunden wurde.

Darüber hinaus kann das Ignorieren von Fehlern zu Sicherheitsausbrüchen führen. Möglicherweise kam die Nullheit von der Tatsache, dass ein Puffer überschrieben wurde oder ähnliches. Nun stürzen Sie nicht ab, was bedeutet, dass der Ausbeuter die Möglichkeit hat, in Ihrem Code auszuführen.

2
Drew Hoskins

Welche Alternativen sehen Sie, um null zurückzugeben?

Ich sehe zwei Fälle:

  • findAnItem (id). Was tun, wenn der Artikel nicht gefunden wird?

In diesem Fall könnten wir: Null zurückgeben oder eine (geprüfte) Ausnahme auslösen (oder möglicherweise ein Element erstellen und zurückgeben)

  • listItemsMatching (Kriterien) Was soll das zurückgeben, wenn nichts gefunden wird?

In diesem Fall könnten wir Null zurückgeben, eine leere Liste zurückgeben oder eine Ausnahme auslösen.

Ich glaube, dass return null möglicherweise weniger gut ist als Alternativen, da der Client daran denken muss, nach null zu suchen, Programmierer vergessen und Code 

x = find();
x.getField();  // bang null pointer exception

In Java ermöglicht RecordNotFoundException, dass der Compiler eine geprüfte Ausnahmebedingung auslöst, den Client an den Fall zu erinnern.

Ich finde, dass Suchen, die leere Listen zurückgeben, recht bequem sein können - füllen Sie einfach den gesamten Inhalt der Liste in der Anzeige aus. Oh, es ist leer.

2
djna

Manchmal ist die Rückgabe von NULL das Richtige, aber gerade wenn Sie mit Sequenzen unterschiedlicher Art (Arrays, Listen, Strings, What-Have-You) zu tun haben, ist es wahrscheinlich besser, eine Null-Sequenz als diese zurückzugeben führt zu kürzerem und hoffentlich verständlicherem Code, während der API-Implementierer nicht viel mehr schreibt.

1
Vatine

Lassen Sie sie eine andere Methode aufrufen, um herauszufinden, ob der vorherige Aufruf null war. ;-) Hey, es war gut genug für JDBC

1
David Plumpton

Die Grundidee hinter diesem Thread ist, defensiv zu programmieren. Das heißt, Code gegen das Unerwartete . Es gibt ein Array mit verschiedenen Antworten:

Adamski schlägt vor, das Null-Objektmuster zu betrachten, wobei diese Antwort für diesen Vorschlag gewählt wurde.

Michael Valenty schlägt auch eine Namenskonvention vor, um dem Entwickler mitzuteilen, was erwartet werden kann . ZeroConcept schlägt eine ordnungsgemäße Verwendung von Exception vor, wenn dies der Grund für NULL ist . Und andere.

Wenn wir die "Regel" formulieren, dass wir immer defensive Programmierung machen wollen, können wir erkennen, dass diese Vorschläge gültig sind.

Wir haben aber zwei Entwicklungsszenarien.

Von einem Entwickler "erstellte" Klassen: Der Autor

Klassen, die von einem anderen (möglicherweise) Entwickler "verbraucht" werden: dem Entwickler

Unabhängig davon, ob eine Klasse für Methoden mit einem Rückgabewert NULL zurückgibt oder nicht, muss der Entwickler prüfen, ob das Objekt gültig ist.

Wenn der Entwickler dies nicht tun kann, ist diese Klasse/Methode nicht deterministisch. Wenn also der "Methodenaufruf", der das Objekt abruft, nicht das tut, was er "ankündigt" (z. B. "getEmployee"), hat er den Vertrag gebrochen.

Als Autor einer Klasse möchte ich beim Erstellen einer Methode immer so freundlich und defensiv (und deterministisch) sein.

In Anbetracht dessen, dass entweder NULL oder das NULLOBJEKT (z. B. if (employee als NullEmployee.ISVALID)) überprüft werden muss Und dies möglicherweise bei einer Sammlung von Employees geschehen muss, ist der Ansatz des Nullobjekts der bessere Ansatz.

Aber ich mag auch Michael Valentys - Vorschlag, die Methode zu benennen, die NULL zurückgeben MUSS, z. B. getEmployeeOrNull.

Ein Autor, der eine Ausnahmebedingung auslöst, hebt die Entscheidung des Entwicklers auf, die Gültigkeit des Objekts zu testen Dies ist für eine Sammlung von Objekten sehr schlecht und zwingt den Entwickler zur Ausnahmebehandlung beim Verzweigen ihres verbrauchenden Codes.

Als Entwickler, der die Klasse konsumiert, hoffe ich, dass der Autor mir die Möglichkeit gibt, die Nullsituation zu vermeiden oder zu programmieren dass ihre Klasse/Methoden damit konfrontiert werden können.

Als Entwickler würde ich also defensiv gegen NULL von einer Methode programmieren. Wenn der Autor mir einen Vertrag gegeben hat, der immer ein Objekt zurückgibt (NULL OBJECT tut dies immer), und dieses Objekt hat eine Methode/Eigenschaft, durch die es möglich ist teste die Gültigkeit des Objekts, Dann würde ich diese Methode/Eigenschaft verwenden, um das Objekt weiter zu verwenden, andernfalls ist das Objekt nicht gültig Ich kann es nicht verwenden.

Fazit: Der Autor der Klasse/Methoden muss Mechanismen zur Verfügung stellen, die ein Entwickler in seiner defensiven Programmierung verwenden kann. Dies ist eine klarere Absicht der Methode.

Der Entwickler sollte immer die defensive Programmierung verwenden, um die Gültigkeit der von einer anderen Klasse/Methode zurückgegebenen Objekte zu testen.

grüße 

GregJF

1
GregJF

Für meinen Anwendungsfall musste ich eine Map from method zurückgeben und dann nach einem bestimmten Schlüssel suchen. Wenn ich jedoch eine leere Map zurückschicke, wird dies zu NullPointerException führen. Dann wird es nicht viel anders sein und null anstelle einer leeren Map zurückgeben. Aber ab Java8 könnten wir Optional verwenden. Dies ist der Grund, warum das optionale Konzept eingeführt wurde.

0
Aman Gupta

Wenn der Code ungefähr so ​​ist:

command = get_something_to_do()

if command:  # if not Null
    command.execute()

Wenn Sie ein Dummy-Objekt haben, dessen Methode execute () nichts tut, und Sie das in den entsprechenden Fällen anstelle von Null zurückgeben, müssen Sie nicht nach dem Null-Fall suchen.

get_something_to_do().execute()

Hier geht es also nicht darum, zwischen NULL und einer Ausnahme zu suchen, sondern darum, dass der Aufrufer spezielle Nicht-Fälle (auf welche Weise auch immer) anders behandeln muss oder nicht.

0
Anon

Andere Optionen dazu sind: Rückgabe eines Werts, der den Erfolg angibt oder nicht (oder den Fehlertyp), aber wenn Sie nur einen booleschen Wert benötigen, der auf Erfolg/Misserfolg hinweist, Null für den Fehler zurückgibt und ein Objekt für den Erfolg wäre nicht weniger korrekt und würde dann true/false zurückgeben und das Objekt über den Parameter abrufen.
Ein anderer Ansatz wäre die Verwendung einer Ausnahme, um Fehler anzuzeigen, aber hier gibt es tatsächlich viele weitere Stimmen, die sagen, dass dies eine schlechte Praxis ist (da die Verwendung von Ausnahmen zwar bequem ist, aber viele Nachteile hat).
Ich persönlich sehe nichts Schlechtes darin, Null als Hinweis darauf zurückzugeben, dass etwas schiefgelaufen ist, und es später zu überprüfen (um tatsächlich zu wissen, ob Sie Erfolg haben oder nicht). Wenn Sie blind denken, dass Ihre Methode nicht NULL zurückgibt und anschließend Ihren Code darauf stützt, kann dies zu anderen, manchmal schwer zu findenden Fehlern führen (obwohl dies in den meisten Fällen nur zum Absturz des Systems führt :), da Sie darauf verweisen 0x00000000 früher oder später).

0
Marcin Deptuła

Während der Entwicklung komplexer Programme können unbeabsichtigte Nullfunktionen auftreten. Wie toter Code deuten solche Vorkommnisse auf gravierende Fehler in Programmstrukturen hin.

Eine Nullfunktion oder -methode wird häufig als Standardverhalten einer revektorfähigen Funktion oder einer überschreibbaren Methode in einem Objekt-Framework verwendet.

Null_Funktion @wikipedia

0
Juicy Scripter

Nun, es hängt sicher von dem Zweck der Methode ab. Manchmal wäre es besser, eine Ausnahme auszulösen. Es hängt alles von Fall zu Fall ab.

0
Denis Biondic