Wie benenne ich eine Methode, die sowohl eine Aufgabe ausführt als auch einen Booleschen Wert als Status zurückgibt?

33

Wenn es eine Methode gibt

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

sollte es eher heißen IsStuffDone()?

Beide Namen können vom Benutzer falsch interpretiert werden: Wenn der Name der DoStuff()Grund ist, wird ein Boolescher Wert zurückgegeben? Wenn der Name ist IsStuffDone(), ist nicht klar, ob die Methode eine Aufgabe ausführt oder nur ihr Ergebnis prüft.

Gibt es eine Konvention für diesen Fall? Oder ein alternativer Ansatz, da dieser als fehlerhaft gilt? Beispielsweise könnte in Sprachen mit Ausgabeparametern wie C # eine boolesche Statusvariable als eine an die Methode übergeben werden, und der Rückgabetyp der Methode wäre void.

BEARBEITEN: In meinem speziellen Problem kann die Ausnahmebehandlung nicht direkt an den Aufrufer delegiert werden, da die Methode Teil einer Schnittstellenimplementierung ist. Daher kann der Aufrufer nicht mit der Behandlung aller Ausnahmen verschiedener Implementierungen beauftragt werden. Diese Ausnahmen sind nicht bekannt. Der Anrufer kann jedoch mit einer benutzerdefinierten Ausnahme umgehen, wie StuffHasNotBeenDoneForSomeReasonExceptionin der Antwort und im Kommentar von npinti vorgeschlagen .

Limbo-Exil
quelle
2
Dies hängt von der Verwendung der Funktion und dem gerade ausgeführten Material ab. Wenn es notwendig ist, dass das Zeug korrekt ausgeführt werden muss, dann würde ich diesen Ansatz als fehlerhaft betrachten, da der Benutzer der Funktion möglicherweise das Boolesche Flag übersieht und auch Informationen fehlen, die von der Ausnahme bereitgestellt werden.
Benni
7
Die meiste Zeit würde ich es als "kaputt" bezeichnen, da booleanes fast immer falsch ist, eine Ausnahme zurückzugeben, anstatt sie zu verpacken oder zu übergeben.
Maaartinus
2
Diese Art von Ausnahmebehandlung ist selten eine gute Idee. Erwarten Sie nur bestimmte Ausnahmen, nicht alle. Und wenn möglich, vermeiden Sie es, es zuerst zu werfen, so dass Sie es nicht mehr fangen müssen.
CodesInChaos
2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? Und um Ihre Frage zu den Ausnahmen zu beantworten: Ich würde sie entweder vom Anrufer behandeln lassen oder abfangen lassen und dann eine benutzerdefinierte Ausnahme auslösen, die bedeutet, dass "diese Methode ihre Aufgabe nicht erfüllt". Teile und genieße.
Bob Jarvis - Reinstate Monica
5
Allen, die sagen: Wirf einfach eine Ausnahme (oder lass sie passieren), treffen Sie unbegründete Annahmen darüber, wie dieser Code verwendet wird. Ein mögliches Szenario ist, dass es ein zu lösendes Problem gibt, mit verschiedenen heuristischen Lösungsmethoden, die immer größere Unterklassen des Problems zu immer höheren Kosten lösen. es wäre sinnvoll so etwas zu schreiben if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Dasselbe mit (benutzerdefinierten?) Ausnahmen zu tun, wäre sinnlos komplizierter.
Marc van Leeuwen

Antworten:

68

In .NET gibt es häufig Methodenpaare, von denen eine möglicherweise eine Ausnahme ( DoStuff) auslöst und die andere einen Booleschen Status und bei erfolgreicher Ausführung das tatsächliche Ergebnis über einen out-Parameter ( TryDoStuff) zurückgibt .

(Microsoft nennt dies das "Try-Parse-Muster" , da die TryParseMethoden verschiedener primitiver Typen möglicherweise das bekannteste Beispiel dafür sind .)

Wenn das TryPräfix in Ihrer Sprache ungewöhnlich ist, sollten Sie es wahrscheinlich nicht verwenden.

ne2dmar
quelle
3
+1 So habe ich so etwas woanders gesehen. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");ist meiner Meinung nach eine ziemlich normale und intuitive Sprache.
Karl Nicoll
12
Es sollte beachtet werden, dass TryDoStuffdavon ausgegangen wird , dass Methoden fehlerfrei sind und keine Nebenwirkungen haben, wenn sie false zurückgeben.
Trillian
Zu Ihrer Information gibt es auch RemoveMethoden, die beispielsweise im .NET Framework ein Element aus einer Datenstruktur entfernen (z. B. Dictionary) und a zurückgeben bool. Der Benutzer der API kann entscheiden, ob er sie verwendet oder ignoriert. Fehler werden jedoch als Ausnahmen gemeldet.
Omer Iqbal
18

Was ist, wenn Sie einfach die Ausnahme zum aufrufenden Code geworfen haben?

Auf diese Weise delegieren Sie die Ausnahmebehandlung an denjenigen, der Ihren Code verwendet. Was ist, wenn Sie in Zukunft Folgendes tun möchten:

  • Wenn keine Ausnahmen ausgelöst werden, führen Sie Aktion A aus
  • Wenn (zum Beispiel) a FileNotFoundExceptiongeworfen wird, führen Sie Aktion B aus
  • Wenn eine andere Ausnahme ausgelöst wird, führen Sie Aktion C aus

Wenn Sie Ihre Ausnahme zurückwerfen, würde die obige Änderung einfach das Hinzufügen eines zusätzlichen catchBlocks bedeuten. Wenn Sie es so lassen, wie es ist, müssen Sie die Methode und den Ort, von dem aus die Methode aufgerufen wird, ändern. Dies kann sich je nach Komplexität des Projekts an mehreren Orten befinden.

npinti
quelle
1
Was ist, wenn es sich um eine Ausnahme handelt, die nicht delegiert werden kann und intern behandelt werden sollte?
Limbo Exile
2
@LimboExile: Ich denke, du könntest trotzdem intern damit umgehen und dann vielleicht eine andere Ausnahme auslösen, vielleicht deine eigene. Dies würde veranschaulichen, dass etwas hätte nicht passieren dürfen, aber gleichzeitig haben Sie nicht offengelegt, was wirklich unter der Haube vor sich geht. Ich gehe davon aus, dass Sie sich deshalb intern mit der Ausnahme befassen möchten.
npinti
6
Es lohnt sich vielleicht daran zu denken, dass das Werfen einer Ausnahme für einen Ausnahmefall sein sollte . Wenn es üblich oder normal ist DoStuff, dass ein Fehler auftritt, entspricht das Auslösen einer Ausnahme für den Fehlerfall dem Steuern des Programmflusses mit schlechten Ausnahmen. Ausnahmen haben auch einen inhärenten Leistungsaufwand. Wenn DoStuffein Fehler ein ungewöhnlicher Fall ist, sind Ausnahmen auf jeden Fall der richtige Weg, wie @npinti vorschlägt.
Karl Nicoll
1
@KarlNicoll Ob etwas "außergewöhnlich" ist, ist völlig subjektiv. Die Hauptkriterien sollten sein, ob das Programm abstürzen soll, wenn niemand etwas gegen den Fehler unternimmt, und ob die Möglichkeit eines Fehlers im Typ der Funktion vorhanden sein soll. Außerdem ist es keine Tatsache, dass Ausnahmen teuer sind. es kommt auf die sprache an und wie man sie wirft. Ausnahmen sind in Python billig, und wenn Sie die Stack-Trace-Informationen entfernen, können sie auch in Java billig sein. Selbst wenn Performancekosten entstanden sind, ist es eine vorzeitige Optimierung, sich darum zu kümmern, bis Sie ein Profil erstellt haben.
Doval
1
@Doval - Ich stimme zu, dass es subjektiv ist, weshalb OP die Antwort von npinti nicht gänzlich ignorieren sollte, da dies die beste Vorgehensweise sein könnte. Mein Punkt war, dass das Werfen von Ausnahmen nicht immer der beste Weg ist. In vielen Fällen kann ein Fehler nicht zum Auslösen einer Ausnahme und möglicherweise zum Absturz einer Anwendung führen. Wenn beispielsweise die OP- DoStuff()Methode bereinigt, nachdem der innere Code eine Ausnahme ausgelöst hat und die Post-Bedingungen der Methode noch immer korrekt sind, ist ein Rückkehrcode möglicherweise eine bessere Option, da der Fehler behoben wurde.
Karl Nicoll
7

DoStuff() ist ausreichend, und der zurückgegebene Wert der Funktion sollte dokumentiert werden, und Sie müssen ihn nicht im Funktionsnamen erwähnen, um nach einer Vielzahl von verfügbaren APIs zu suchen:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )
amd
quelle
6

In Java definiert die Collection- API eine Add-Methode , die einen Booleschen Wert zurückgibt. Grundsätzlich wird zurückgegeben, ob das Hinzufügen die Sammlung geändert hat. Für a Listwird normalerweise true zurückgegeben, da das Element hinzugefügt wurde, während für a Setfalse zurückgegeben werden kann, wenn das Element bereits vorhanden war, da Sets jedes eindeutige Element höchstens einmal zulässt.

Das heißt, wenn Sie ein Element hinzufügen, das von der Auflistung nicht zugelassen ist, z. B. einen Nullwert, wird beim Hinzufügen NullPointerExceptioneher ein als ein False zurückgegeben.

Warum müssen Sie einen Booleschen Wert zurückgeben, wenn Sie dieselbe Logik auf Ihren Fall anwenden? Wenn eine Ausnahme ausgeblendet werden soll, tun Sie dies nicht. Wirf einfach die Ausnahme. Auf diese Weise können Sie sehen, was schief gelaufen ist. Wenn Sie einen Booleschen Wert benötigen, weil dies möglicherweise aus einem anderen Grund als einer Ausnahme nicht erfolgt ist, benennen Sie die Methode DoStuff(), da dies das Verhalten darstellt. Es macht Sachen.

mrjink
quelle
1

Könnte aus verschiedenen Gründen fehlschlagen, Ausnahme, normale Bedingungen, so würde dies verwenden:

boolean doStuff() - returns true when successful

Wichtig ist zu dokumentieren, was der Boolesche Wert bedeutet.

user136354
quelle
1

Normalerweise habe ich Methoden, die ein OperationResult(oder OperationResult<T>) zurückgeben und die IsSuccessEigenschaft auf das OperationResultentsprechende setzen. Wenn die 'usual'-Methode ungültig ist, geben Sie zurück OperationResult, wenn die' usual'-Methode ein Objekt zurückgibt, geben Sie zurück OperationResult<T>und setzen Sie die ItemEigenschaft auf den OperationResultentsprechenden Wert. Ich würde auch vorschlagen, Methoden OperationResultwie .Failed(string reason)und zu haben .Succeeded(T item). OperationResultwird schnell zu einem vertrauten Typ in Ihren Systemen und Entwickler lernen, wie man damit umgeht.

Scott Rickman
quelle
0

Es hängt wirklich von der Sprache ab, die Sie verwenden. Wie für einige Sprachen gibt es Konventionen und Protokolle, die in vielen Sprachen oder überhaupt nicht existieren.

In der Objective-C-Sprache und im iOS / Mac OS X SDK lauten die Methodennamen normalerweise:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Wie Sie sehen, gibt es dort eine Methode namens viewDidLoad. Ich bevorzuge diese Art der Benennung, wenn ich meine eigenen Anwendungen erstelle. Dies könnte verwendet werden, um zu überprüfen, ob etwas passieren sollte, wie Sie sagten. Ich glaube, dass Sie zu tief darüber nachdenken, wie Sie Ihre Methoden nennen. Die meisten Methoden geben den Status der von ihnen ausgeführten Aktionen zurück. Beispiel:

In PHP gibt mysql_connect () false zurück, wenn die Verbindung fehlschlägt. Es sagt nicht, dass es so sein wird, aber es tut es und die Dokumentation sagt, dass es so ist, dass es für den Programmierer nicht falsch interpretiert werden kann.

Gergy008
quelle
Ah, aber viewDidLoad ist eher eine Rückrufbenachrichtigung. Benutzer rufen es nicht auf, um die Ansicht zu laden. Sie wird vielmehr aufgerufen, nachdem die Ansicht geladen wurde. Ebenso: isVisible legt die Sichtbarkeit nicht fest, sondern gibt nur den aktuellen Wert zurück. Es ist nicht tut nichts.
Lilbyrdie
0

Ich möchte vorschlagen, das Präfix "Try" zu verwenden. Dies ist ein bekanntes Muster für Situationen, in denen die Methode erfolgreich ist oder nicht. Dieses Benennungsmuster wurde von Microsoft in .NET Framework verwendet (z. B. TryParseInt).

Aber vielleicht geht es hier nicht um Benennung. Es geht darum, wie Sie die Eingabe- / Ausgabeparameter Ihrer Methode entwerfen.

Meine Idee ist, dass, wenn eine Methode einen Rückgabewert hat, der Zweck des Aufrufs dieser Methode darin bestehen sollte, diesen Rückgabewert zu erhalten. Verwenden Sie daher keinen Rückgabetyp, um den Status einer Operation anzuzeigen.

In C # bevorzuge ich die Verwendung eines out-Parameters. Mit einem Out markiere ich den Parameter als Seitenparameter. Insbesondere unterstützt C # 6 die Inline-Variablendeklaration.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.
000
quelle
0

Ich würde einen Namen auswählen, der auf der primären Rolle der Methode basiert. Die Tatsache, dass diese Methode einen booleschen Wert zurückgibt, erfordert nicht das Präfix 'Is'. Lassen Sie uns etwas Java-Code haben, der das Präfix 'Is' ziemlich ausführlich verwendet. Schau es dir an java.io.File.delete(). Es kehrt zurück boolean, wird aber nicht aufgerufen isFileDeleted(). Der Grund ist, dass die Hauptaufgabe der Metode darin besteht, eine Datei zu löschen. Jemand ruft diese Methode mit der Absicht auf, eine Datei zu löschen.

Der Boolesche Wert dient nur dazu, das Ergebnis der Arbeit wiederzugeben. Auf der anderen Seite wollen wir mal sehen java.util.Map.isEmpty(). Offensichtlich rufen Sie eine solche Methode auf, um herauszufinden, ob die Sammlung leer ist. Du willst keine Sachen machen. Sie möchten nur herausfinden, wie der aktuelle Status ist.

Also persönlich würde ich definitiv dabei bleiben DoStuff().

Das ist für mich ein sehr wichtiges Thema. Oft stoße ich bei der Pflege von Legacy-Code auf etwas, das ich persönlich als "Lügenmethode" bezeichne. Der Name weist auf die Aktion A hin, der Body jedoch auf die Aktion B. Das bizarrste Beispiel ist eine Getter-Methode, mit der Daten in der Datenbank geändert werden.

Jacek Prucia
quelle