Ist es empfehlenswert, Funktionen nur zum Zentralisieren von häufig verwendetem Code zu verwenden?

20

Ich stoße häufig auf dieses Problem. Zum Beispiel schreibe ich derzeit eine Lesefunktion und eine Schreibfunktion, und beide prüfen, ob bufes sich um einen NULL-Zeiger handelt und ob sich die modeVariable innerhalb bestimmter Grenzen befindet.

Dies ist Code-Duplikation. Dies kann gelöst werden, indem es in seine eigene Funktion versetzt wird. Aber soll ich? Dies wird eine ziemlich anämische Funktion sein (macht nicht viel), eher lokalisiert (also nicht für allgemeine Zwecke) und steht nicht gut für sich allein (kann nicht herausfinden, wofür Sie es brauchen, es sei denn, Sie sehen, wo es ist gebraucht). Eine andere Möglichkeit ist die Verwendung eines Makros, aber ich möchte in diesem Beitrag auf Funktionen eingehen.

Sollten Sie für so etwas eine Funktion verwenden? Was sind die Vor- und Nachteile?

EpsilonVector
quelle
2
Einzeiliges Material muss mehr als nur an mehreren Stellen verwendet werden, um den Wechsel in eine separate Funktion zu rechtfertigen.
1
Gute Frage. Ich habe mich das schon so oft gefragt.
Rdasxy

Antworten:

31

Dies ist eine großartige Nutzung von Funktionen.

Dies wird eine ziemlich anämische Funktion sein (macht nicht viel) ...

Das ist gut. Funktionen sollten nur eines tun.

eher lokalisiert ...

Machen Sie es in einer OO-Sprache privat.

(also nicht für allgemeine Zwecke)

Wenn es mehr als einen Fall behandelt, ist es ein allgemeiner Zweck. Darüber hinaus ist die Verallgemeinerung nicht die einzige Verwendung von Funktionen. Sie dienen in der Tat dazu, (1) dass Sie denselben Code mehr als einmal schreiben müssen, und (2) den Code in kleinere Teile aufzuteilen, um ihn besser lesbar zu machen. In diesem Fall werden sowohl (1) als auch (2) erreicht. Selbst wenn Ihre Funktion von nur einer Stelle aus aufgerufen wurde, kann sie dennoch bei (2) helfen.

und steht nicht gut für sich allein (kann nicht herausfinden, wofür Sie es benötigen, es sei denn, Sie sehen, wo es verwendet wird).

Überlegen Sie sich einen guten Namen, und er steht für sich allein. "ValidateFileParameters" oder so. Jetzt steht gut für sich.

Kramii setzt Monica wieder ein
quelle
6
Nun waren Punkte. Ich würde (3) hinzufügen, um zu verhindern, dass Sie nach einem doppelten Fehler in Ihrer gesamten Codebasis suchen, wenn Sie ihn an einer Stelle beheben können. Das Duplizieren von Code kann sehr schnell zu Wartungsarbeiten führen.
Deadalnix
2
@deadalnix: Guter Punkt. Als ich schrieb, stellte ich fest, dass meine Punkte eine grobe Übervereinfachung waren. Schreiben und einfacheres Debuggen sind sicherlich ein Vorteil der Aufteilung von Dingen in Funktionen (ebenso wie die Fähigkeit, separate Funktionen in einem Stück zu testen).
Kramii setzt Monica
11

Es sollte also total eine Funktion sein.

if (isBufferValid(buffer)) {
    // ...
}

Deutlich lesbarer und wartbarer (wenn sich die Prüflogik jemals ändert, ändern Sie sie nur an einer Stelle).

Außerdem werden solche Dinge leicht eingebunden, so dass Sie sich nicht einmal Gedanken über Funktionsaufruf-Overheads machen müssen.

Lass mich dir eine bessere Frage stellen. Wie ist das nicht eine gute Übung?

Tue das Richtige. :)

Yam Marcovic
quelle
4
Wenn isBufferValid einfach return buffer != null;ist, verletzen Sie dort die Lesbarkeit.
pdr
5
@pdr: In diesem einfachen Fall schadet es nur der Lesbarkeit, wenn Sie eine Einstellung eines Kontrollfreaks haben und WIRKLICH wollen, wie der Code überprüft, ob der Puffer gültig ist. Also ist es in diesen einfachen Fällen subjektiv.
Spoike
4
@pdr Ich weiß nicht, wie es der Lesbarkeit schadet. Sie befreien andere Entwickler davon, sich darum zu kümmern, wie Sie etwas tun, und konzentrieren sich darauf, was Sie tun. isBufferValidist definitiv lesbarer (in meinem Buch) als buffer != null, weil es den Zweck klarer kommuniziert. Ganz zu schweigen davon, dass Sie auch hier vor Duplikaten geschützt sind. Was brauchst du noch?
Yam Marcovic
5

IMO, Codeausschnitte sind es wert, in ihre eigenen Funktionen verschoben zu werden, wenn dies die Lesbarkeit des Codes verbessert , unabhängig davon, ob die Funktion sehr kurz ist oder nur einmal verwendet wird.

Natürlich gibt es Grenzen, die vom gesunden Menschenverstand vorgegeben werden. Sie möchten die WriteToConsole(text)Methode nicht haben, wenn der Körper Console.WriteLine(text)zum Beispiel einfach ist. Es ist jedoch eine gute Praxis, sich in der Lesbarkeit zu irren.

Konamiman
quelle
2

Im Allgemeinen ist es eine gute Idee, Funktionen zu verwenden, um Doppelungen im Code zu entfernen.

Allerdings kann es zu weit treiben . Dies ist ein Urteilsspruch.

Am Beispiel Ihrer Nullpufferprüfung würde ich wahrscheinlich sagen, dass der folgende Code klar genug ist und nicht in eine separate Funktion extrahiert werden sollte, selbst wenn an einigen Stellen dasselbe Muster verwendet wird.

if (buf==null) throw new BufferException("Null read buffer!");

Wenn Sie die Fehlermeldung als Parameter für eine generische Nullprüffunktion angeben und den Code berücksichtigen, der zum Definieren der Funktion erforderlich ist, ist dies keine Netto-LOC-Einsparung. Ersetzen Sie dies durch:

checkForNullBuffer(buf, "Null read buffer!");

Darüber hinaus bedeutet das Eintauchen in die Funktion, um zu sehen, was sie beim Debuggen tut, dass der Funktionsaufruf für den Benutzer weniger "transparent" ist und daher als weniger lesbar / wartbar angesehen werden kann.

mikera
quelle
Ihr Beispiel sagt wohl mehr über den Mangel an Vertragsunterstützung in der Sprache aus als über ein echtes Bedürfnis nach Vervielfältigung. Aber trotzdem gute Punkte.
Deadalnix
1
Du hast den Punkt so verpasst, wie ich es sehe. Es ist das, wonach ich suche , nicht jedes Mal über die Logik nachdenken zu müssen, wenn Sie debuggen . Wenn ich wissen will, wie es gemacht wird, überprüfe ich es einmal. Es ist einfach albern, es jedes Mal tun zu müssen.
Yam Marcovic
Wenn die Fehlermeldung ("Null Read Buffer") wiederholt wird, sollte diese Verdoppelung unbedingt beseitigt werden.
Kevin Cline
Nun, es ist nur ein Beispiel, aber vermutlich haben Sie an jeder Funktionsaufrufstelle eine andere Nachricht - z. B. "Null-Lesepuffer" und "Null-Schreibpuffer". Sofern Sie nicht für jede Situation die gleichen Protokoll- / Fehlermeldungen wünschen, können Sie sie nicht de-duplizieren .......
mikera
-1 Voraussetzungen.checkNotNull ist eine gute und keine schlechte Vorgehensweise. Es ist jedoch nicht erforderlich, die Zeichenfolgenachricht einzuschließen. google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234 20.11.11
2

Das Zentralisieren des Codes ist normalerweise immer eine gute Idee. Wir müssen den Code so oft wie möglich wiederverwenden.

Es ist jedoch wichtig zu wissen, wie das geht. Wenn Sie zum Beispiel einen Code haben, der compute_prime_number () oder check_if_packet_is_bad () ausführt, ist er gut. Es besteht die Möglichkeit, dass sich der Algorithmus der Funktionalität selbst weiterentwickelt und davon profitiert.

Jeder Code, der sich als Prosa wiederholt, kann jedoch nicht sofort zentralisiert werden. Das ist schlecht. Sie können beliebige Codezeilen in einer Funktion ausblenden, um einen Code im Laufe der Zeit auszublenden. Wenn mehrere Teile der Anwendung verwendet werden, müssen sie alle mit den Anforderungen aller Angerufenen der Funktion kompatibel bleiben.

Hier sind einige Fragen, die Sie stellen sollten, bevor Sie sie stellen

  1. Hat die Funktion, die Sie erstellen, eine eigene inhärente Bedeutung, oder handelt es sich nur um ein Bündel von Zeilen?

  2. In welchem ​​anderen Kontext müssen dieselben Funktionen verwendet werden? Ist es wahrscheinlich, dass Sie die API etwas verallgemeinern müssen, bevor Sie diese verwenden?

  3. Welche Erwartungen haben (verschiedene Teile der) Anwendungen, wenn Sie Ausnahmen auslösen?

  4. In welchen Szenarien werden sich die Funktionen weiterentwickeln?

Sie sollten auch prüfen, ob solche Sachen bereits existieren. Ich habe so viele Leute gesehen, die immer dazu neigten, ihre Makros MIN, MAX neu zu definieren, anstatt nach dem zu suchen, was bereits existiert.

Im Wesentlichen stellt sich die Frage: "Ist diese neue Funktion wirklich wiederverwendbar, oder handelt es sich nur um Kopieren und Einfügen ?" Wenn es das erste ist, ist es gut zu gehen.

Dipan Mehta
quelle
1
Nun, wenn der duplizierte Code keine eigene Bedeutung hat, sagt Ihnen Ihr Code, dass er umgestaltet werden muss. Weil Orte, an denen die Vervielfältigung auftritt, wahrscheinlich auch keine eigene Bedeutung haben.
Deadalnix
1

Codeduplizierung sollte vermieden werden. Jedes Mal, wenn Sie damit rechnen, sollten Sie eine Codeduplizierung vermeiden. Wenn Sie nicht damit gerechnet haben, wenden Sie die Regel 3: refactor an, bevor derselbe Code dreimal dupliziert wird.

Was ist eine Codeduplizierung?

  • Eine Routine, die an mehreren Stellen in der Codebasis wiederholt wird. Dieses Tourine muss komplexer sein als ein einfacher Funktionsaufruf (andernfalls können Sie durch Faktorisieren nichts gewinnen).
  • Eine sehr häufige, auch triviale Aufgabe (oft ein Test). Dies verbessert die Kapselung und Semantik im Code.

Betrachten Sie das folgende Beispiel:

if(user.getPrileges().contains("admin")) {
    // Do something
}

wird

if(user.isAdmin()) {
    // Do something
}

Sie haben die Kapselung (jetzt können Sie die Bedingungen für den Status eines Administrators transparent ändern) und die Semantik des Codes verbessert. Wenn ein Fehler entdeckt wird, der darin besteht, dass Sie überprüfen, ob der Benutzer ein Administrator ist, müssen Sie nicht Ihre gesamte Codebasis ausgeben und überall eine Korrektur vornehmen (wodurch das Risiko besteht, einen zu vergessen und eine Sicherheitslücke in Ihrer Anwendung zu bekommen).

deadalnix
quelle
1
Übrigens veranschaulicht das Gesetz von Demeter.
17.
1

Bei DRY geht es darum, die Code-Manipulation zu vereinfachen. Sie haben gerade einen feinen Punkt über dieses Prinzip angesprochen: Es geht nicht darum, die Anzahl der Token in Ihrem Code zu minimieren, sondern vielmehr darum, einzelne Änderungspunkte für semantisch äquivalenten Code zu erstellen . Es hört sich so an, als ob Ihre Prüfungen immer dieselbe Semantik haben. Sie sollten daher in eine Funktion eingefügt werden, falls Sie sie ändern müssen.

l0b0
quelle
0

Wenn Sie es doppelt sehen, sollten Sie einen Weg finden, es zu zentralisieren.

Funktionen sind ein guter Weg (vielleicht nicht der beste, aber das hängt von der Sprache ab). Auch wenn die Funktion anämisch ist, heißt das nicht, dass dies auch so bleibt.

Was ist, wenn Sie auch nach etwas anderem suchen müssen?

Werden Sie alle Stellen finden, an denen Sie den zusätzlichen Check hinzufügen oder einfach eine Funktion ändern müssen?

Vielen Dank an Papathanasiou
quelle
... auf der anderen Seite, was ist, wenn es eine sehr hohe Wahrscheinlichkeit gibt, dass Sie nie etwas hinzufügen. Ist Ihr Vorschlag auch in diesem Fall noch die beste Vorgehensweise?
EpsilonVector
1
@EpsilonVector Meine Faustregel lautet: Wenn ich es einmal ändern muss, kann ich es genauso gut umgestalten. Also, in deinem Fall würde ich es verlassen und wenn ich es ändern müsste, würde es eine Funktion werden.
Thanos Papathanasiou
0

Es ist fast immer gut, wenn folgende Bedingungen erfüllt sind:

  • Verbessert die Lesbarkeit
  • in begrenztem Umfang verwendet

In größerem Umfang müssen Sie die Duplizierung sorgfältig gegen die Abhängigkeitsabwägungen abwägen. Beispiele für Techniken zur Einschränkung des Gültigkeitsbereichs: Verstecken in privaten Bereichen oder Modulen, ohne diese öffentlich zugänglich zu machen.

Beschädigen
quelle