Entwerfen datenbankbezogener Methoden, die besser zurückgegeben werden können: true / false oder betroffene Zeile?

10

Ich habe einige Methoden, die einige Datenänderungen in einer Datenbank durchführen (Einfügen, Aktualisieren und Löschen). Das ORM, für das ich vom Rückgabewert betroffene int-Werte für diese Art von Methode verwende. Was soll ich für "meine Methode" zurückgeben, um den Erfolgs- / Fehlerstatus der Operation anzuzeigen?

Betrachten Sie den Code, der Folgendes zurückgibt int:

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

Dann Verwendung:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

Vergleichen Sie mit boolean:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

Dann Verwendung:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

Welches (A oder B) ist besser? Oder Vor- / Nachteile von jedem?

Hoang Tran
quelle
In Ihrem "A.2". Wenn Nullzeilen betroffen sind, warum ist dies ein Fehler, wenn Nullzeilen betroffen sein müssen? Mit anderen Worten, wenn kein Datenbankfehler vorliegt, warum ist dies ein Fehler?
Jaydee
5
Gibt es einen semantischen Unterschied zwischen "erfolglos" und "betroffene Nullzeilen"? Wenn beispielsweise alle Bestellungen eines Kunden gelöscht werden, besteht ein Unterschied zwischen "Kunde existiert nicht" und "Kunde hat keine Bestellungen".
Kopffüßer
Betrachten Sie ein Löschen mit null Zeilen als unerwartet? In diesem Fall werfen.
usr
@Arian Ich denke das ist die eigentliche Frage für mich. Ich glaube, ich habe B gewählt, weil mein Code bei A jetzt an einigen Stellen nach 0 und an anderen nach -1
sucht

Antworten:

18

Eine andere Möglichkeit besteht darin, ein Ergebnisobjekt anstelle von Basistypen zurückzugeben. Zum Beispiel:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

Wenn Sie aus irgendeinem Grund die Anzahl der betroffenen Zeilen zurückgeben müssen, können Sie einfach eine Methode in OperationResult hinzufügen:

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

Dieses Design ermöglicht es Ihrem System, zu wachsen und neue Funktionen (Kenntnis der betroffenen Zeilen) aufzunehmen, ohne den vorhandenen Code zu ändern.

AlfredoCasado
quelle
2
+1 "Dieses Design ermöglicht es Ihrem System, zu wachsen und neue Funktionen (Kenntnis der betroffenen Zeilen) aufzunehmen, ohne vorhandenen Code zu ändern." Dies ist die richtige Art, über diese Kategorie von Fragen nachzudenken.
Informiert
Ähnliches habe ich in meinem alten Projekt gemacht. Ich habe Wrapper Request and Result Objekt. Beide verwenden Kompositionen, um weitere Details aufzunehmen. und beide haben auch grundlegende Daten. In diesem Fall hat das Ergebnisobjekt einen Statuscode und ein Nachrichtenfeld.
Informiert
Insbesondere für ein System mit ORM würde ich diese Best Practice nennen. Das betreffende ORM kann auch bereits solche Ergebnisobjekttypen enthalten!
Brian S
Wenn ich mir nur das OPs-Beispiel ansehe, kann ich mir nur vorstellen, dass deleteById irgendwann 2 zurückgibt :) also definitiv ein benutzerdefinierter Typ, ja.
Deadsven
Ich mag diesen Ansatz. Ich denke, es ist nicht blockierend, deckt beide Ansätze ab und ist modifizierbar / erweiterbar (in Zukunft, falls erforderlich). Der einzige Nachteil, den ich mir vorstellen kann, ist, dass es ein bisschen mehr Code ist, besonders wenn es mitten in meinem Projekt ist. Ich werde dies als Antwort markieren. Vielen Dank.
Hoang Tran
12

Die Rückgabe der Anzahl der betroffenen Zeilen ist besser, da hier zusätzliche Informationen zum Ablauf des Vorgangs angezeigt werden.

Kein Programmierer wird Ihnen die Schuld geben, weil er / sie dies schreiben muss, um zu überprüfen, ob er / sie während des Vorgangs einige Änderungen erhalten hat:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

Aber sie werden Ihnen die Schuld geben, wenn sie die Anzahl der betroffenen Zeilen kennen müssen und erkennen, dass es keine Methode gibt, um diese Anzahl zu erhalten.

Übrigens: Wenn Sie mit "Fehler" einen syntaktischen Abfragefehler meinen (in diesem Fall ist die Anzahl der betroffenen Zeilen offensichtlich 0), ist das Auslösen der Ausnahme angemessener.

Bobby
quelle
4
-1 wegen des Grundes, den Sie angegeben haben. Zusätzliche Informationen zu geben ist nicht immer eine gute Idee. Oft führte ein besseres Design dazu, dass dem Anrufer genau das mitgeteilt wurde, was er benötigt, und nicht mehr.
Arseni Mourzenko
1
@MainMa nichts steht der Erstellung überladener Methoden im Wege. Darüber hinaus kann in Muttersprachen (z. B. C-Familie) die Anzahl der Zeilen direkt in der Logik verwendet werden (0 ist falsch, alles andere ist wahr).
PTwr
@PTwr: Um mit der Tatsache fertig zu werden, dass die Methode zu viele Informationen zurückgibt, schlagen Sie vor, eine Überladung zu erstellen? Das scheint nicht richtig zu sein. Was "Null ist falsch, Nicht-Null ist wahr" betrifft, so ist dies nicht der Punkt meines Kommentars und in einigen Sprachen eine schlechte Praxis.
Arseni Mourzenko
1
Wahrscheinlich bin ich einer der verwöhnten Programmierer, denn ich glaube nicht, dass das Verwenden von Tricks als gute Praxis angesehen werden kann. In der Tat, wenn ich mit dem Code jemand anderes zu tun haben , ich bin dankbar, wer es geschrieben hat und hat nicht beschäftigen keine Tricks.
Proskor
4
Sie sollten hier immer die Anzahl der betroffenen Zeilen zurückgeben !!! Da Sie unmöglich wissen können, ob die Anzahl der Zeilen = 0 ein Fehler ist oder ob die Anzahl der Zeilen = 3 ein Erfolg ist? Wenn jemand 3 Zeilen einfügen möchte und nur 2 eingefügt werden, würden Sie true zurückgeben, aber es ist nicht richtig! Und wenn jemand update t set category = 'default' where category IS NULLsogar 0 betroffene Zeilen haben möchte, wäre das ein Erfolg, denn jetzt gibt es kein Element ohne Kategorie, auch wenn keine Zeilen betroffen wären!
Falco
10

Ich würde keinen von ihnen empfehlen. Geben Sie stattdessen bei Erfolg nichts (void) zurück und lösen Sie bei einem Fehler eine Ausnahme aus.

Dies ist genau der gleiche Grund, warum ich bestimmte Mitglieder der Klasse als privat deklariere. Es erleichtert auch die Verwendung der Funktion. Mehr operativer Kontext bedeutet nicht immer besser, aber sicherlich komplexer. Je weniger Sie versprechen, desto mehr abstrahieren Sie, desto leichter ist es für den Kunden zu verstehen und desto mehr Freiheit haben Sie bei der Auswahl der Implementierung.

Die Frage ist, wie Erfolg / Fehler angezeigt werden kann. In diesem Fall reicht es aus, einen Fehler durch Auslösen einer Ausnahme zu signalisieren und bei Erfolg nichts zurückzugeben. Warum muss ich mehr bereitstellen, als der Benutzer benötigt?

Fehler / Ausnahmesituationen können auftreten und Sie müssen sich dann mit ihnen befassen. Ob Sie try / catch verwenden oder die Rückkehrcodes überprüfen, ist eine Frage des Stils / der persönlichen Präferenz. Die Ideen hinter try / catch sind: Trennen Sie den normalen Fluss vom außergewöhnlichen Fluss und lassen Sie Ausnahmen bis zu der Ebene sprudeln, wo sie am besten gehandhabt werden können. Wie viele bereits betont haben, hängt es also davon ab, ob ein Fehler wirklich außergewöhnlich ist oder nicht.

Proskor
quelle
4
-1 Warum sollten Sie nichts zurückgeben, wo Sie etwas zurückgeben könnten, ohne negative Nebenwirkungen, das zusätzlichen operativen Kontext bietet?
FreeAsInBeer
7
Aus genau dem gleichen Grund entscheide ich mich, bestimmte Mitglieder der Klasse für privat zu erklären. Es erleichtert auch die Verwendung der Funktion. Mehr operativer Kontext bedeutet nicht immer besser, aber sicherlich komplexer. Je weniger Sie versprechen, desto mehr abstrahieren Sie, desto leichter ist es für den Kunden zu verstehen und desto mehr Freiheit haben Sie bei der Auswahl der Implementierung.
Proskor
1
Welche Daten benötigt der Benutzer tatsächlich? Die Frage ist, wie Erfolg / Fehler angezeigt werden kann. In diesem Fall reicht es aus, einen Fehler durch Auslösen einer Ausnahme zu signalisieren und bei Erfolg nichts zurückzugeben. Warum muss ich mehr bereitstellen, als der Benutzer benötigt?
Proskor
4
@proskor: Ausnahmen gelten für Ausnahmefälle. "Fehler" in diesem Szenario kann ein erwartetes Ergebnis sein. Stellen Sie dies auf jeden Fall als mögliche Alternative vor, aber es gibt hier nicht genügend Informationen, um eine Empfehlung abzugeben.
Nick Barnes
1
-1 Ausnahmen sollten nicht Teil des normalen Programmablaufs sein. Es ist unklar, was "Fehler" im Kontext Ihres Fehlers bedeutet, aber eine Ausnahme im Kontext eines Datenbankaufrufs sollte auf eine Ausnahme zurückzuführen sein, die in der Datenbank auftritt. Das Beeinflussen von Nullzeilen sollte keine Ausnahme sein. Eine verstümmelte Abfrage, die nicht analysiert werden kann, auf eine nicht vorhandene Tabelle verweist usw. wäre eine Ausnahme, da das Datenbankmodul daran ersticken und auslösen würde.
2

"Ist das besser als das?" ist keine nützliche Frage, wenn die beiden Alternativen nicht dasselbe tun.

Wenn Sie die Anzahl der betroffenen Zeilen kennen müssen , müssen Sie Version A verwenden. Wenn Sie dies nicht müssen, können Sie Version B verwenden. Alle Vorteile, die Sie möglicherweise durch weniger Aufwand beim Schreiben von Code erzielen, sind jedoch bereits weg Sie haben sich die Mühe gemacht, beide Versionen in einem Online-Forum zu veröffentlichen!

Mein Punkt ist: Welche Lösung besser ist, hängt ganz von Ihren Anforderungen speziell für diese Anwendung ab, und Sie kennen diese Umstände viel besser als wir. Es gibt keine branchenweite, benutzerfreundliche, bewährte Methode, die im Allgemeinen nicht besser ist . du musst selbst darüber nachdenken. Und für eine Entscheidung, die so einfach zu überarbeiten ist wie diese, müssen Sie auch nicht allzu viel Zeit mit Nachdenken verbringen.

Kilian Foth
quelle
Ich bin damit einverstanden, dass dies stark von den App-Anforderungen abhängt. Es scheint jedoch, dass diese Art von Situation nicht sehr einzigartig ist, und ich suche nicht nach einer Silberkugel, sondern nur nach der Erfahrung anderer, mit der gleichen / ähnlichen Sache umzugehen (vielleicht ist die Frage etwas irreführend, ich mag andere Vorschläge als A. / B)
Hoang Tran
1

Zwei der wichtigsten Prinzipien beim wartbaren Software-Design sind KISS und YAGNI .

  • KISS : Halte es einfach, dumm
  • YAGNI : Du wirst es nicht brauchen

Es ist so gut wie nie eine gute Idee , in der Logik setzen Sie nicht sofort brauchen gerade jetzt . Jeff Atwood (Mitbegründer von StackExchange) hat unter vielen anderen darüber geschrieben , und meiner Erfahrung nach haben er und andere Befürworter dieser Konzepte völlig Recht.

Jede Komplexität, die Sie einem Programm hinzufügen, ist mit Kosten verbunden, die über einen langen Zeitraum bezahlt werden. Das Programm wird schwieriger zu lesen, komplexer zu ändern und leichter für Fehler einzuschleichen. Fallen Sie nicht in die Falle, Dinge "nur für den Fall" hinzuzufügen. Es ist ein falsches Sicherheitsgefühl.

Sie werden selten beim ersten Mal einen Code richtig machen. Änderungen sind unvermeidlich; Das Hinzufügen einer spekulativen Logik zur defensiven Vorbereitung auf unbekannte zukünftige Eventualitäten schützt Sie nicht davor, Ihren Code umgestalten zu müssen, wenn sich herausstellt, dass die Zukunft anders verläuft als erwartet. Die Wartung unnötiger / bedingter Logik ist eher ein Problem der Wartbarkeit als eine spätere Umgestaltung, um fehlende Funktionen hinzuzufügen.

Da Ihr Programm derzeit nur wissen muss, ob die Operation erfolgreich war oder fehlgeschlagen ist, ist Ihre vorgeschlagene Lösung B (Rückgabe eines einzelnen Booleschen Werts) der richtige Ansatz. Sie können es später jederzeit umgestalten, wenn sich die Anforderung ändert. Diese Lösung ist die einfachste und hat die geringste Komplexität (KISS) und macht genau das, was Sie brauchen, und nichts weiter (YAGNI).

Ben Lee
quelle
-1

Die ganzen Zeilen oder ein Fehlerstatus

Ziehen Sie in Betracht, die gesamten Zeilen zumindest als Laufzeitoption zurückzugeben. In DB-Einfügungen müssen Sie möglicherweise die eingefügten Daten überprüfen, da sie sich häufig von den Daten unterscheiden, die Sie an die DB gesendet haben. Zu den gängigen Beispielen gehören automatisch generierte Zeilen-IDs (die die App wahrscheinlich sofort benötigt), von der Datenbank festgelegte Standardwerte und Ergebnisse von Triggern, wenn Sie diese verwenden.

Auf der anderen Seite, wenn Sie die zurückgegebenen Daten nicht benötigen, dann brauchen Sie auch nicht die betroffene Zeilenanzahl, da es für das Ergebnis von 0. nicht hilfreich, wenn es Fehler gibt, dann müssen Sie zurückkommen, welche Art von Es ist ein Fehler aufgetreten, der mit den Grundsätzen Ihres Projekts zur Fehlerbehandlung übereinstimmt (Ausnahmen, numerische Fehlercodes usw.). Es gibt jedoch gültige Abfragen, die sich korrekt auf 0 Zeilen auswirken (dh "Alle abgelaufenen Bestellungen löschen", wenn tatsächlich keine vorhanden sind).

Peter ist
quelle