Ich weiß, dass dies sehr anwendungsfallspezifisch sein kann, aber ich frage mich viel zu oft. Gibt es eine allgemein bevorzugte Syntax?
Ich frage nicht, was der beste Ansatz ist, wenn ich in einer Funktion bin. Ich frage, ob ich vorzeitig beenden oder die Funktion einfach nicht aufrufen soll.
Wrap, wenn um Funktionsaufruf
if (shouldThisRun) {
runFunction();
}
Habe wenn ( Wache ) in Funktion
runFunction() {
if (!shouldThisRun) return;
}
Die letztere Option hat offensichtlich das Potenzial, die Codeduplizierung zu reduzieren, wenn diese Funktion mehrmals aufgerufen wird. Manchmal fühlt es sich jedoch falsch an, sie hier hinzuzufügen, da Sie möglicherweise die Einzelverantwortung der Funktion verlieren .
Hier ist ein Beispiel
Wenn ich eine updateStatus () -Funktion habe, die einfach den Status von etwas aktualisiert. Ich möchte den Status nur aktualisieren, wenn sich der Status geändert hat. Ich kenne die Stellen in meinem Code, an denen sich der Status ändern kann, und ich kenne andere Stellen, an denen er sich trotzig geändert hat.
Ich bin mir nicht sicher, ob es nur ich ist, aber es fühlt sich etwas schmutzig an, diese Innenfunktion zu überprüfen, da ich diese Funktion so rein wie möglich halten möchte - wenn ich sie aufrufe, erwarte ich, dass der Status aktualisiert wird. Aber ich kann nicht sagen, ob es besser ist, den Anruf an den wenigen Stellen, an denen ich weiß, dass er sich möglicherweise nicht geändert hat, in einen Scheck zu packen.
quelle
Antworten:
Umschließen eines Funktionsaufrufs:
Dies dient dazu, zu entscheiden, ob die Funktion überhaupt aufgerufen werden soll, und ist Teil des Entscheidungsprozesses Ihres Programms.
Guard-Klausel in Funktion (Early Return):
Dies dient zum Schutz vor dem Aufruf mit ungültigen Parametern
Eine auf diese Weise verwendete Schutzklausel hält die Funktion "rein" (Ihr Begriff). Es ist nur vorhanden, um sicherzustellen, dass die Funktion nicht mit fehlerhaften Eingabedaten unterbrochen wird.
Die Logik, ob die Funktion überhaupt aufgerufen werden soll, liegt auf einer höheren Abstraktionsebene, wenn auch nur gerecht. Es sollte außerhalb der Funktion selbst existieren. Wie DocBrown sagt, können Sie eine Zwischenfunktion haben, die diese Prüfung durchführt, um den Code zu vereinfachen.
Dies ist eine gute Frage, die zu den Fragen gehört, die zum Erkennen von Abstraktionsebenen führen. Jede Funktion sollte auf einer einzelnen Abstraktionsebene arbeiten, und sowohl die Programmlogik als auch die Funktionslogik in der Funktion zu haben, fühlt sich für Sie falsch an - dies liegt daran, dass sie sich auf verschiedenen Abstraktionsebenen befinden. Die Funktion selbst ist eine niedrigere Ebene.
Indem Sie diese getrennt halten, stellen Sie sicher, dass Ihr Code einfacher zu schreiben, zu lesen und zu warten ist.
quelle
Sie können beides haben - eine Funktion, die keine Parameter überprüft, und eine andere, die dies tut (möglicherweise einige Informationen darüber zurückgeben, ob der Aufruf ausgeführt wurde):
Auf diese Weise können Sie doppelte Logik vermeiden, indem Sie eine wiederverwendbare
tryRunFunction
Funktion bereitstellen und trotzdem Ihre ursprüngliche (möglicherweise reine) Funktion verwenden, die die Prüfung nicht im Inneren durchführt.Beachten Sie, dass Sie manchmal eine Funktion wie
tryRunFunction
bei einem integrierten Scheck ausschließlich benötigen , damit Sie den Scheck in integrieren könnenrunFunction
. Oder Sie müssen die Prüfung an keiner Stelle in Ihrem Programm erneut verwenden. In diesem Fall können Sie sie in der aufrufenden Funktion belassen.Versuchen Sie jedoch, dem Aufrufer transparent zu machen, was passiert, indem Sie Ihren Funktionen Eigennamen geben. Aufrufer müssen also nicht raten oder in die Implementierung schauen, wenn sie die Überprüfungen selbst durchführen müssen oder wenn die aufgerufene Funktion dies bereits für sie tut. Ein einfaches Präfix wie
try
kann hierfür oft ausreichen.quelle
runFunction
. Eine Funktion wieupdateStatus()
kann von einer anderen Funktion wie begleitet seinupdateIfStatusHasChanged()
. Dies ist jedoch zu 100% fallabhängig. Es gibt keine "One-Size-Fits-All" -Lösung. Ja, ich stimme zu, die "Try" -Sprache ist nicht immer eine gute Wahl.Wer entscheidet, ob er läuft, lautet die Antwort von GRASP , wer der "Informationsexperte" ist, der es weiß.
Wenn Sie sich dazu entschlossen haben, sollten Sie die Funktion aus Gründen der Übersichtlichkeit umbenennen.
So etwas, wenn die Funktion entscheidet:
Oder wenn der Anrufer entscheiden soll:
quelle
Ich möchte die Antwort von @ Baldrickk erweitern.
Es gibt keine allgemeine Antwort auf Ihre Frage. Dies hängt von der Bedeutung (Vertrag) der aufzurufenden Funktion und der Art der Bedingung ab.
Lassen Sie es uns im Kontext Ihres Beispielaufrufs diskutieren
updateStatus()
. Der Vertrag besteht wahrscheinlich darin, einen Status zu aktualisieren, da etwas passiert ist, das Einfluss auf den Status hat. Ich würde erwarten, dass Aufrufe dieser Methode zulässig sind, auch wenn es keine echte Statusänderung gibt, und notwendig, wenn es eine echte Änderung gibt.Eine anrufende Site kann also einen Anruf überspringen,
updateStatus()
wenn sie weiß, dass (innerhalb ihres Domänenhorizonts) nichts Relevantes geändert wurde. In diesen Situationen sollte der Aufruf von einem geeignetenif
Konstrukt umgeben sein.Innerhalb der
updateStatus()
Funktion kann es Situationen geben, in denen diese Funktion (anhand von Daten innerhalb ihres Domänenhorizonts) erkennt, dass nichts zu tun ist, und dort sollte sie frühzeitig zurückkehren.Die Fragen sind also:
Mit einer
updateStatus()
Funktion würde ich erwarten, beide zu sehen, Sites aufzurufen, die wissen, dass sich nichts geändert hat, den Aufruf zu überspringen und die Implementierung frühzeitig auf "nichts geänderte" Situationen zu prüfen, selbst wenn auf diese Weise dieselbe Bedingung manchmal zweimal überprüft wird, beide innen und außen.quelle
Es gibt viele gute Erklärungen. Aber ich möchte ungewöhnlich aussehen: Angenommen, Sie verwenden diesen Weg:
Und Sie müssen eine andere Funktion in einer
runFunction
Methode wie dieser aufrufen :Was wirst du tun? Kopieren Sie alle Validierungen von oben nach unten?
Das glaube ich nicht. Daher mache ich normalerweise den gleichen Ansatz: Eingaben validieren und Bedingungen in der
public
Methode überprüfen . Öffentliche Methoden sollten ihre eigenen Validierungen durchführen und die erforderlichen Bedingungen überprüfen, die selbst Anrufer erfüllen. Aber lassen Sie private Methoden einfach ihr eigenes Geschäft machen . Eine andere Funktion wird möglicherweise aufgerufen,runFunction
ohne eine Validierung durchzuführen oder eine Bedingung zu überprüfen.quelle