it-swarm.dev

Einzeilige Funktionen, die nur einmal aufgerufen werden

Stellen Sie sich eine parameterlose (edit : nicht unbedingt) Funktion vor, die eine einzelne Codezeile ausführt und nur einmal im Programm aufgerufen wird (obwohl es nicht unmöglich ist, dass sie in Zukunft erneut benötigt wird). .

Es könnte eine Abfrage durchführen, einige Werte überprüfen, etwas mit Regex tun ... alles, was dunkel oder "hackig" ist.

Das Grundprinzip dahinter wäre, kaum lesbare Bewertungen zu vermeiden:

if (getCondition()) {
    // do stuff
}

dabei ist getCondition() die einzeilige Funktion.

Meine Frage ist einfach: Ist das eine gute Praxis? Es scheint mir in Ordnung zu sein, aber ich weiß nicht auf lange Sicht ...

122
vemv

Kommt auf diese eine Zeile an. Wenn die Zeile für sich selbst lesbar und prägnant ist, wird die Funktion möglicherweise nicht benötigt. Einfaches Beispiel:

void printNewLine() {
  System.out.println();
}

OTOH, wenn die Funktion einer Codezeile, die z.B. Ein komplexer, schwer zu lesender Ausdruck, der (für mich) vollkommen gerechtfertigt ist. Erfundenes Beispiel (zur besseren Lesbarkeit in mehrere Zeilen unterteilt):

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
242
Péter Török

Ja, dies kann verwendet werden, um Best Practices zu erfüllen. Zum Beispiel ist es besser, eine klar benannte Funktion einige Arbeiten ausführen zu lassen, selbst wenn sie nur eine Zeile lang ist, als diese Codezeile in einer größeren Funktion zu haben und einen einzeiligen Kommentar zu benötigen, der erklärt, was sie tut. Außerdem sollten benachbarte Codezeilen Aufgaben auf derselben Abstraktionsebene ausführen. Ein Gegenbeispiel wäre so etwas wie

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

In diesem Fall ist es definitiv besser, die Mittellinie in eine vernünftig benannte Funktion zu verschieben.

67
Kilian Foth

Ich denke, dass eine solche Funktion in vielen Fällen ein guter Stil ist, aber Sie können eine lokale boolesche Variable als Alternative in Fällen betrachten, in denen Sie diese Bedingung nicht irgendwo an anderen Orten verwenden müssen, z.

bool someConditionSatisfied = [complex expression];

Dies gibt dem Codeleser einen Hinweis und erspart die Einführung einer neuen Funktion.

37
cybevnm

Zusätzlich zu Peters Antwort , wenn diese Bedingung möglicherweise zu einem späteren Zeitpunkt aktualisiert werden muss, indem sie so gekapselt wird, wie Sie es vorschlagen, dass Sie nur einen einzigen Bearbeitungspunkt haben würden.

Nach Peters Beispiel, wenn dies

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

wird dies

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

Sie nehmen eine einzelne Bearbeitung vor und diese wird universell aktualisiert. In Bezug auf die Wartbarkeit ist dies ein Plus.

In Bezug auf die Leistung entfernen die meisten optimierenden Compiler den Funktionsaufruf und fügen den kleinen Codeblock trotzdem ein. Wenn Sie so etwas optimieren, kann die Blockgröße tatsächlich verkleinert werden (indem die für den Funktionsaufruf, die Rückgabe usw. erforderlichen Anweisungen gelöscht werden), sodass dies normalerweise auch unter Bedingungen sicher ist, die andernfalls das Inlining verhindern könnten.

23
Stephen

Zusätzlich zur Lesbarkeit (oder als Ergänzung dazu) können Funktionen auf der richtigen Abstraktionsebene geschrieben werden.

5
tylermac

Es hängt davon ab, ob. Manchmal ist es besser, einen Ausdruck in eine Funktion/Methode zu kapseln, selbst wenn es sich nur um eine Zeile handelt. Wenn es kompliziert zu lesen ist oder Sie es an mehreren Stellen benötigen, halte ich es für eine gute Praxis. Langfristig ist es einfacher zu pflegen, da Sie ein einzelner Änderungspunkt und bessere Lesbarkeit eingeführt haben.

Manchmal ist es jedoch nur etwas, das Sie nicht brauchen. Wenn der Ausdruck trotzdem leicht zu lesen ist und/oder nur an einer Stelle erscheint, wickeln Sie ihn nicht ein.

4
Falcon

Ich denke, wenn Sie nur einige davon haben, ist es in Ordnung, aber das Problem tritt auf, wenn viele davon in Ihrem Code enthalten sind. Und wenn der Compiler ausgeführt wird oder der Interpitor (abhängig von der verwendeten Sprache), wird diese Funktion im Speicher ausgeführt. Nehmen wir also an, Sie haben 3 davon. Ich glaube nicht, dass der Computer es bemerken wird, aber wenn Sie anfangen, 100 dieser kleinen Dinge zu haben, muss das System Funktionen im Speicher registrieren, die nur einmal aufgerufen und dann nicht zerstört werden.

3
WojonsTech

Ich habe genau das erst kürzlich in einer Anwendung getan, die ich überarbeitet habe, um die tatsächliche Bedeutung des Codes ohne Kommentare deutlich zu machen:

protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}
3
joshperry

Es gibt bereits viele gute Antworten, aber es gibt einen erwähnenswerten Sonderfall .

Wenn Ihre einzeilige Anweisung einen Kommentar benötigt und Sie ihren Zweck eindeutig identifizieren können (was bedeutet: Name), sollten Sie eine Funktion extrahieren, während Sie sie erweitern der Kommentar in API-Dokument. Auf diese Weise machen Sie den Funktionsaufruf schneller und verständlicher.

Interessanterweise kann das Gleiche getan werden, wenn derzeit nichts zu tun ist, sondern ein Kommentar, der an die erforderlichen Erweiterungen erinnert (in naher Zukunft)1)), also das,

def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

könnte genauso gut in diese geändert werden

def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) Sie sollten sich darüber wirklich sicher sein (siehe YAGNI Prinzip)

0
Wolf

Wenn die Sprache dies unterstützt, verwende ich normalerweise beschriftete anonyme Funktionen, um dies zu erreichen.

someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

IMHO ist dies ein guter Kompromiss, da Sie den Vorteil der Lesbarkeit haben, dass der komplizierte Ausdruck die Bedingung if nicht überfüllt, während der globale/Paket-Namespace nicht mit kleinen Wegwerfetiketten überladen wird. Es hat den zusätzlichen Vorteil, dass die Funktion "Definition" genau dort ist, wo sie verwendet wird, was das Ändern und Lesen der Definition erleichtert.

Es müssen nicht nur Prädikatfunktionen sein. Ich mag es, wiederholte Kesselplatten auch in kleine Funktionen wie diese einzuschließen (es funktioniert besonders gut für die Erstellung von Pythonic-Listen, ohne die Syntax der Klammern zu überladen). Beispiel: Das folgende stark vereinfachte Beispiel für die Arbeit mit PIL in Python

#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]
0
crasic

Wenn Sie diese eine Zeile in eine gut benannte Methode verschieben, ist Ihr Code leichter zu lesen. Viele andere haben das bereits erwähnt ("selbstdokumentierender Code"). Der andere Vorteil des Verschiebens in eine Methode besteht darin, dass der Unit-Test einfacher ist. Wenn es in eine eigene Methode isoliert und auf eine Einheit getestet wurde, können Sie sicher sein, dass ein Fehler, der gefunden wird, nicht in dieser Methode enthalten ist.

0
Andrew