Was ist der Vorteil einer Funktion ohne Parameter, die nur eine andere Funktion aufruft?

21

Ein Tutorial (für Javascript) schlägt vor, dass wir eine Funktion wie diese schreiben:

function sayHello() {
   //Some comments explaining the next line
   window.alert("Hello");
}

Abgesehen von der Verschleierung hat das Schreiben im wirklichen Leben Vorteile? Wenn ja, welche Vorteile ergeben sich daraus?

Daniel
quelle
14
Es wird wahrscheinlich nur versucht, Ihnen zu zeigen, wie Sie Ihre eigenen Funktionen definieren.
Doval
4
Ich würde niemals Code so schreiben, dass er verschleiert wird - Code ist für den Programmierer einfach zu lesen, nicht umgekehrt. Verwenden Sie einen Verschleierer zur Verschleierung.
Rhughes
32
Der Vorteil ist, dass eine Funktion mit Parametern umhüllt wird. Auf diese Weise müssen Sie nur an einer Stelle "Hallo" in "Ola" ändern, wenn Sie Ihre App später spanisch machen möchten.
Pieter B
9
@ DieterB eigentlich "Hola"
rev
29
@PieterB - Und wenn Sie feststellen, dass Sie "Hola" falsch geschrieben haben, müssen Sie es nur an einer Stelle reparieren.
Daniel R Hicks

Antworten:

60

Bitte entschuldigen Sie mein Gedächtnis, wenn ich diese falsche ... Javascript ist nicht meine bevorzugte Implementierungssprache.

Es gibt mehrere Gründe, warum man möchte, dass eine No-Arg-Funktion einen anderen Funktionsaufruf umschließt. Während der einfache Anruf window.alert("Hello");etwas ist, das Sie sich vorstellen können, anstatt direkt anzurufen sayHello().

Aber was ist, wenn mehr dahinter steckt? Sie haben ein Dutzend Orte, an denen Sie anrufen möchten, sayHello()und haben window.alert("Hello");stattdessen geschrieben . Jetzt möchten Sie, dass es a window.alert("Hello, it is now " + new Date()). Wenn Sie alle diese Anrufe umbrochen haben, während sayHello()Sie sie an einer Stelle ändern. Wenn nicht, ändern Sie es an ein Dutzend Stellen. Dies berührt Don't Repeat Yourself . Sie tun es, weil Sie es in Zukunft nicht ein Dutzend Mal tun müssen möchten.

Ich habe in der Vergangenheit mit einer i18n / l10n- Bibliothek gearbeitet, die Funktionen zur clientseitigen Lokalisierung des Texts verwendete. Betrachten Sie die sayHello()Funktion. Sie können es ausdrucken lassen, holawenn der Benutzer in eine spanische Sprache lokalisiert ist. Das könnte ungefähr so ​​aussehen:

function sayHello() {
  var language = window.navigator.userLanguage || window.navigator.language;
  if(language === 'es') { window.alert('Hola'); }
   else { window.alert("Hello"); }
}

Allerdings hat die Bibliothek nicht so funktioniert. Stattdessen hatte es eine Reihe von Dateien, die so aussahen:

# English file
greeting = hello
# Spanish file
greeting = hola

Anschließend erkennt die Bibliothek die Spracheinstellung des Browsers und erstellt dynamische Funktionen mit der entsprechenden Lokalisierung als Rückgabewert für einen nicht berechtigten Funktionsaufruf basierend auf der entsprechenden Lokalisierungsdatei.

Ich bin nicht genug von einem Javascript-Programmierer, um zu sagen, ob das gut oder schlecht ist ... nur, dass es ein möglicher Ansatz war und sein kann.

Der Punkt ist, den Aufruf einer anderen Funktion in eine eigene Funktion zu verpacken. Dies ist oft sehr nützlich und hilft bei der Modularisierung der Anwendung. Außerdem kann dies zu einer besseren Lesbarkeit des Codes führen.

Abgesehen davon arbeiten Sie an einem Tutorial. Am Anfang gilt es, die Dinge so einfach wie möglich vorzustellen. Die Einführung von Funktionsaufrufen im varargs-Stil von Anfang an kann für eine Person, die mit der Codierung im Allgemeinen nicht vertraut ist, zu sehr verwirrendem Code führen. Es ist viel einfacher, von keinen Argumenten zu Argumenten und zu Varagrammen zu wechseln - mit jedem Aufbau auf den vorherigen Beispielen und dem Verständnis.

zzzzBov
quelle
3
window.alertwird auch oft als Platzhalter während der Entwicklung verwendet, bis ein nettes Modal / Popup entworfen / implementiert werden kann, so dass es, genau wie das Sprachproblem, viel einfacher ausgetauscht werden kann. Wenn Sie bereits eine haben, sind für ein Design-Update einige Jahre später möglicherweise ähnliche Änderungen erforderlich.
Izkata
34

Ich denke, es ist manchmal nützlich, um die Implementierung zu verbergen.

 function sayHello() {
  window.alert("Hello");
 }

Und das gibt Ihnen die Flexibilität, es später zu ändern

 function sayHello() {
  console.log("Hello");
 }
Sleiman Jneidi
quelle
19

Abgesehen von der Verschleierung hat das Schreiben im wirklichen Leben Vorteile? Wenn ja, welche Vorteile ergeben sich daraus?

  • Zentralisierung: Obwohl die Implementierung nur eine Zeile lang ist, ziehen Sie es wahrscheinlich vor, diese Zeile an einer einzigen Stelle zu ändern, wenn sich diese häufig ändert, als überall dort, wo sayHello aufgerufen wird.

  • Minimieren / Ausblenden von Abhängigkeiten: Ihr Client-Code muss nicht mehr wissen, dass es ein Fensterobjekt gibt, und Sie können sogar die gesamte Implementierung ändern, ohne den Client-Code zu beeinflussen

  • Vertragserfüllung: Client-Code kann ein Modul erwarten, das eine sayHello-Funktion hat. In diesem Fall muss die Funktion vorhanden sein, auch wenn sie trivial ist.

  • Konsistenz von Abstraktionsebenen: Wenn Client-Code Operationen auf hoher Ebene verwendet, liegt es in Ihrem Interesse, Client-Code in Form von "sayHello", "sayBye" und einigen anderen "sayXXX" -Funktionen anstelle von Fensterobjekten zu schreiben. Tatsächlich möchten Sie im Client-Code möglicherweise nicht einmal wissen, dass es so etwas wie ein "Fenster" -Objekt gibt.

utnapistim
quelle
Ich mag diese Antwort. Häufig ist das Design der Hauptgrund für diese Funktionen, insbesondere für das OO-Design, bei dem Objekte, die für das Verhalten verantwortlich sind, Funktionen anderer Objekte mit einem bestimmten Verhalten ausführen sollen. Stellen Sie sich Ihr Auto vor, insbesondere das Schalten. Sie schalten Ihren Gang hoch, indem Sie Ihren Steuerknüppel anweisen, hoch zu schalten. Dieser Anruf wird wiederum an Ihr Getriebe weitergeleitet. Zusätzlich wird überprüft, ob Sie von Ihrer aktuellen Position aufsteigen können.
Eric
3
Die anderen Gründe sind gut, aber +1 für die Erwähnung von Abstraktionsebenen. Die Verwendung klarer Abstraktionen und guter Funktionsnamen erleichtert das Lesen und Verwalten von altem Code erheblich. An dem Punkt, an dem sayHello () aufgerufen wird, geht es Ihnen nicht unbedingt darum, wie es implementiert wird, sondern Sie möchten nur verstehen, was die Absicht dieser Codezeile ist. Und ein Funktionsname wie sayHello () macht das offensichtlich.
kkrambo
Ebenfalls +1 für die Erwähnung von Abstraktionsebenen: Es kann vorkommen, dass die Implementierung einer Funktion auf einer Abstraktionsebene aus einem Aufruf einer anderen Funktion auf einer niedrigeren Abstraktionsebene besteht. Na und?
Giorgio
7

Erstaunlich, dass keine andere Person das Testen erwähnt hat.

Die spezielle "umbrochene" Zeile, die Sie ausgewählt haben window.alert('hello'), ist tatsächlich ein perfektes Beispiel dafür. Alles, was mit dem windowObjekt zu tun hat, ist wirklich sehr, sehr schmerzhaft zu testen. Multiplizieren Sie dies mit 1000 in Ihrer Anwendung, und ich garantiere, dass die Entwickler das Testen irgendwann aufgeben werden. Andererseits ist es ziemlich einfach, die sayHelloFunktion mit einem Spion zu zerstören und zu testen, ob sie aufgerufen wurde.

Ein praktischeres Beispiel - denn wer verwendet eigentlich window.alert(...)in der Produktion Code? - überprüft die Systemuhr. Dies würde beispielsweise DateTime.Nowin .NET, time(...)C / C ++ oder System.currentTimeMillis()Java erfolgen. Sie möchten diese wirklich in eine Abhängigkeit einschließen, die Sie injizieren können, da sie nicht nur (fast) unmöglich zu verspotten sind, sondern auch schreibgeschützt und nicht deterministisch sind . Jeder Test, der eine Funktion oder Methode abdeckt, bei der die Systemuhrfunktion direkt verwendet wird, weist mit hoher Wahrscheinlichkeit intermittierende und / oder zufällige Fehler auf.

Der eigentliche Wrapper ist eine 1-Zeilen-Funktion - return DateTime.Now- aber das ist alles, was Sie brauchen, um ein schlecht gestaltetes, nicht testbares Objekt sauber und testbar zu machen. Sie können den Wrapper durch eine gefälschte Uhr ersetzen und die Uhrzeit nach Belieben einstellen. Problem gelöst.

Aaronaught
quelle
2

Wie Doval sagte, versucht dieses Beispiel wahrscheinlich nur, Ihnen Funktionen vorzustellen. Ich bin allgemein, aber es ist nützlich. Insbesondere wenn Sie einige, aber nicht alle Argumente angeben und die anderen übergeben, können Sie aus einer allgemeineren Funktion eine fallspezifischere Funktion erstellen. Betrachten Sie als etwas triviales Beispiel eine Sortierfunktion, die ein Array zum Sortieren und eine Komparatorfunktion zum Sortieren benötigt. Durch die Angabe einer Komparatorfunktion, die mit einem numerischen Wert verglichen wird, kann eine sortByNumericalValue-Funktion erstellt werden, und Aufrufe dieser Funktion sind viel klarer und präziser.

raptortech97
quelle
2

Das Denken hinter Ihrer Frage scheint zu sein: "Warum nicht einfach alert("Hello");direkt schreiben ? Es ist ziemlich einfach."

Die Antwort ist zum Teil, weil Sie nicht wirklich anrufen möchten alert("Hello")- Sie möchten nur Hallo sagen.

Oder: Warum Kontakte auf Ihrem Telefon speichern, wenn Sie nur Telefonnummern wählen können? Weil Sie sich nicht alle diese Zahlen merken wollen; weil das Wählen von Nummern mühsam und fehleranfällig ist; weil sich die Zahl ändern könnte, aber es ist immer noch die gleiche Person am anderen Ende. Weil Sie Leute anrufen wollen , keine Nummern.

Dies wird unter Begriffen wie Abstraktion, Indirektion, "Verbergen von Implementierungsdetails" und sogar "Ausdruckscode" verstanden.

Die gleiche Überlegung gilt für die Verwendung von Konstanten, anstatt überall rohe Werte zu schreiben. Sie können schreiben Sie einfach 3.141592...jedes Mal , wenn π brauchen, aber zum Glück gibt es Math.PI.

Wir könnten uns auch selbst anschauen alert(). Wen interessiert es, wie es diesen Alarmdialog erstellt und anzeigt? Sie möchten nur den Benutzer benachrichtigen. Zwischen dem Schreiben alert("Hello")und dem Ändern der Pixel auf Ihrem Bildschirm gibt es einen tiefen, tiefen Stapel von Code und Hardware, wobei jede Ebene der nächsten mitteilt, was sie will, und die nächste Ebene sich um Details kümmert, bis die tiefstmögliche Ebene einige Bits in der Ebene umdreht Videospeicher.

Sie möchten das alles wirklich nicht selbst tun müssen, nur um Hallo zu sagen.

Bei der Programmierung - eigentlich bei jeder Aufgabe - geht es darum, komplexe Probleme in überschaubare Teile zu unterteilen. Wenn Sie jeden Block lösen, erhalten Sie einen Baustein, und mit genügend einfachen Bausteinen können Sie große Dinge bauen.

Es ist Algebra.

Flambino
quelle
-1

Es ist eine gute Idee, die Berechnung von Werten (Ausdrücken) von der Ausführung von Aktionen (Anweisungen) getrennt zu halten . Wir wollen genau steuern, wo und wann Maßnahmen ergriffen werden (wie das Anzeigen von Nachrichten), aber bei der Berechnung von Werten arbeiten wir lieber auf einer abstrakteren Ebene und müssen uns nicht darum kümmern, wie diese Werte berechnet werden.

Eine Funktion, die nur einen Rückgabewert unter Verwendung der angegebenen Argumente berechnet, heißt pure .

Eine "Funktion", die eine Aktion ausführt, ist eigentlich eine Prozedur , die eine Wirkung hat .

Effekte, die während der Berechnung eines Wertes verursacht werden, werden als Nebenwirkungen bezeichnet . Es ist besser, sie nach Möglichkeit zu vermeiden ("Ich brauchte nur diesen String, ich wusste nicht, dass er die Datenbank hämmern würde!").

Um die Wahrscheinlichkeit von Nebenwirkungen zu minimieren, sollten wir vermeiden, zu viele Daten an unsere Verfahren zu senden oder Berechnungen anzustellen. Wenn eine Berechnung im Voraus durchgeführt werden muss, ist es normalerweise besser, sie separat in einer reinen Funktion durchzuführen und dann nur das erforderliche Ergebnis an die Prozedur zu übergeben. Dies hält den Zweck des Verfahrens klar und verringert die Wahrscheinlichkeit, dass es später als Teil einer Berechnung wiederverwendet wird (die reine Funktion kann stattdessen wiederverwendet werden).

Aus dem gleichen Grund sollten wir vermeiden, Ergebnisse innerhalb einer Prozedur zu verarbeiten. Es ist besser, das Ergebnis (falls vorhanden) unserer Aktion zurückzugeben und eine nachfolgende Verarbeitung mit reinen Funktionen durchzuführen.

Wenn wir diese Regeln befolgen, erhalten wir möglicherweise eine Prozedur wie die sayHello, die keine Daten benötigt und kein Ergebnis hat. Daher ist es die beste Schnittstelle, keine Argumente zu haben und keinen Wert zurückzugeben. Dies ist beispielsweise vorzuziehen, wenn Sie "console.log" mitten in einer Berechnung aufrufen.

Um die Notwendigkeit von Effekten während der Berechnung zu verringern, können Berechnungen durchgeführt werden, die Prozeduren zurückgeben . z.B. Wenn wir uns für eine Aktion entscheiden müssen, kann eine reine Funktion eine Prozedur auswählen und zurückgeben, anstatt sie direkt auszuführen.

Um den Rechenaufwand bei Prozeduren zu verringern, können Prozeduren auch andere Prozeduren als Parameter verwenden (möglicherweise das Ergebnis einer Funktion). z.B. eine Reihe von Prozeduren nehmen und nacheinander ausführen.

Warbo
quelle
Sie sprechen sich hier offenbar gegen OOP aus, wo das Grundprinzip "Tell, Don't Ask" lautet. Den Ratschlägen hier zu folgen, führt normalerweise zu Code, der mit nutzlosen "getFoo" -, "setFoo" - und "execute" -Aufrufen durchsetzt ist. Bei OOP geht es darum , Daten und Verhalten zu kombinieren , nicht sie zu trennen.
Aaronaught
@Aaronaught Es ist wahr, dass ich gegen OOP bin, aber ich würde auf keinen Fall setFooAnrufe empfehlen , da dies veränderbare Daten impliziert. Das Ändern des Inhalts von Variablen / Eigenschaften ist ein Effekt, der dazu führt, dass alle Berechnungen, die diese Daten verwenden, unrein werden. Ich würde keine executeAufrufe per se empfehlen , aber ich würderunFoo Verfahren zum Ausführen von Aktionen basierend auf ihren Argumenten empfehlen .
Warbo
-2

Ich würde sagen, es ist eine Kombination der Antworten von @MichaelT und @Sleiman Jneidi.

Vielleicht gibt es eine Reihe von Stellen im Code, an denen Sie den Benutzer mit einer Hallo-Nachricht begrüßen möchten, damit Sie diese Idee in eine Methode packen. In der Methode können Sie es übersetzen oder ein einfaches 'Hallo' erweitern, indem Sie einen längeren Text anzeigen. Vielleicht möchten Sie auch ein nettes JQuery-Dialogfeld anstelle einer Warnung verwenden.

Der Punkt ist, dass sich die Implementierung an einem Ort befindet. Sie müssen nur den Code an einer Stelle ändern. (SayHello () ist vielleicht ein zu einfaches Beispiel)

paul
quelle
@downvoter (s) - teilen Sie Ihr Wissen mit dem Rest von uns. Ist mein Beitrag einfach falsch, schlecht geschrieben oder was?
Paul