Hier ist ein Programmier- / Sprachproblem, über das ich gerne Ihre Gedanken hören würde.
Wir haben Konventionen entwickelt, die die meisten Programmierer befolgen sollten, die nicht Teil der Sprachsyntax sind, aber dazu dienen, den Code besser lesbar zu machen. Diese sind natürlich immer umstritten, aber es gibt zumindest einige Kernkonzepte, die den meisten Programmierern zusagen. Benennen Sie Ihre Variablen entsprechend, benennen Sie sie im Allgemeinen, machen Sie Ihre Zeilen nicht zu lang, vermeiden Sie lange Funktionen, Kapseln und dergleichen.
Es gibt jedoch ein Problem, zu dem ich noch keinen Kommentar gefunden habe, und das könnte das größte des ganzen Haufens sein. Es ist das Problem, dass Argumente beim Aufrufen einer Funktion anonym bleiben.
Funktionen stammen aus der Mathematik, in der f (x) eine eindeutige Bedeutung hat, weil eine Funktion eine viel strengere Definition hat als normalerweise beim Programmieren. Reine Funktionen in der Mathematik können viel weniger als sie in der Programmierung können und sie sind ein viel eleganteres Werkzeug, sie nehmen normalerweise nur ein Argument (normalerweise eine Zahl) und sie geben immer einen Wert (normalerweise auch eine Zahl) zurück. Wenn eine Funktion mehrere Argumente akzeptiert, handelt es sich fast immer nur um zusätzliche Dimensionen der Funktionsdomäne. Mit anderen Worten, ein Argument ist nicht wichtiger als die anderen. Sicher, sie sind explizit geordnet, aber ansonsten haben sie keine semantische Ordnung.
Bei der Programmierung haben wir jedoch mehr Freiheitsgrade bei der Definition von Funktionen, und in diesem Fall würde ich behaupten, dass dies keine gute Sache ist. In einer normalen Situation haben Sie eine Funktion wie diese definiert
func DrawRectangleClipped (rectToDraw, fillColor, clippingRect) {}
Betrachtet man die Definition, so ist bei korrekter Schreibweise der Funktion klar, was was ist. Wenn Sie die Funktion aufrufen, kann es sein, dass in Ihrer IDE / Ihrem Editor Intellisense- / Code-Vervollständigungsmagie vor sich geht, die Ihnen sagt, was das nächste Argument sein soll. Aber warte. Wenn ich das brauche, während ich den Anruf schreibe, fehlt hier nicht etwas? Die Person, die den Code liest, hat nicht den Vorteil einer IDE, und wenn sie nicht zur Definition springt, weiß sie nicht, welches der beiden als Argumente übergebenen Rechtecke wofür verwendet wird.
Das Problem geht noch weiter. Wenn unsere Argumente von einer lokalen Variablen stammen, kann es vorkommen, dass wir nicht einmal wissen, was das zweite Argument ist, da wir nur den Variablennamen sehen. Nehmen Sie zum Beispiel diese Codezeile
DrawRectangleClipped(deserializedArray[0], deserializedArray[1], deserializedArray[2])
Dies wird in unterschiedlichem Maße in verschiedenen Sprachen, aber auch in streng typisierten Sprachen, vermieden. Selbst wenn Sie Ihre Variablen vernünftig benennen, erwähnen Sie nicht einmal den Typ der Variablen, wenn Sie sie an die Funktion übergeben.
Wie bei der Programmierung üblich, gibt es viele mögliche Lösungen für dieses Problem. Viele sind bereits in gängigen Sprachen implementiert. Zum Beispiel benannte Parameter in C #. Alles, was ich weiß, hat jedoch erhebliche Nachteile. Die Benennung jedes Parameters bei jedem Funktionsaufruf kann möglicherweise nicht zu lesbarem Code führen. Es fühlt sich fast so an, als ob wir über die Möglichkeiten der Klartextprogrammierung hinauswachsen. Wir sind in fast allen Bereichen von NUR Text weggegangen, aber wir codieren immer noch das Gleiche. Weitere Informationen werden benötigt, um im Code angezeigt zu werden? Füge mehr Text hinzu. Wie auch immer, das wird ein bisschen tangential, also höre ich hier auf.
Eine Antwort, die ich auf das zweite Code-Snippet erhielt, war, dass Sie wahrscheinlich zuerst das Array in einige benannte Variablen entpacken und diese dann verwenden würden, aber der Variablenname kann viele Dinge bedeuten, und die Art und Weise, wie er aufgerufen wird, sagt Ihnen nicht unbedingt, wie er sein soll im Kontext der aufgerufenen Funktion interpretiert werden. Im lokalen Bereich haben Sie möglicherweise zwei Rechtecke mit den Namen leftRectangle und rightRectangle, da diese semantisch dargestellt werden, sie müssen jedoch nicht auf das erweitert werden, was sie darstellen, wenn sie einer Funktion zugewiesen werden.
Wenn Ihre Variablen im Kontext der aufgerufenen Funktion benannt werden, geben Sie weniger Informationen ein, als Sie mit diesem Funktionsaufruf möglicherweise könnten, und auf einer bestimmten Ebene führt dies zu einem schlechteren Code. Wenn Sie eine Prozedur haben, die ein Rechteck ergibt, das Sie in rectForClipping speichern, und dann eine andere Prozedur, die rectForDrawing bereitstellt, dann ist der eigentliche Aufruf von DrawRectangleClipped nur eine Zeremonie. Eine Zeile, die nichts Neues bedeutet und nur vorhanden ist, damit der Computer genau weiß, was Sie möchten, obwohl Sie dies bereits mit Ihrer Benennung erklärt haben. Das ist keine gute Sache.
Ich würde wirklich gerne neue Perspektiven dazu hören. Ich bin mir sicher, dass ich nicht der erste bin, der dies als Problem ansieht. Wie wird es also gelöst?
Antworten:
Ich bin damit einverstanden, dass die Art und Weise, wie Funktionen häufig verwendet werden, verwirrend für das Schreiben von Code und insbesondere das Lesen von Code sein kann.
Die Antwort auf dieses Problem hängt zum Teil von der Sprache ab. Wie Sie bereits erwähnt haben, hat C # Parameter benannt. Die Lösung von Objective-C für dieses Problem umfasst aussagekräftigere Methodennamen. Beispielsweise
stringByReplacingOccurrencesOfString:withString:
handelt es sich um eine Methode mit eindeutigen Parametern.In Groovy verwenden einige Funktionen Maps, die eine Syntax wie die folgende zulassen:
Im Allgemeinen können Sie dieses Problem beheben, indem Sie die Anzahl der Parameter begrenzen, die Sie an eine Funktion übergeben. Ich denke 2-3 ist eine gute Grenze. Wenn sich herausstellt, dass eine Funktion mehr Parameter benötigt, muss ich das Design überdenken. Dies kann jedoch allgemein schwieriger zu beantworten sein. Manchmal versuchen Sie, in einer Funktion zu viel zu tun. Manchmal ist es sinnvoll, eine Klasse zum Speichern Ihrer Parameter in Betracht zu ziehen. In der Praxis stelle ich häufig fest, dass Funktionen, die eine große Anzahl von Parametern annehmen, normalerweise viele davon als optional haben.
Selbst in einer Sprache wie Objective-C ist es sinnvoll, die Anzahl der Parameter zu begrenzen. Ein Grund ist, dass viele Parameter optional sind. Ein Beispiel finden Sie unter rangeOfString: und seinen Variationen in NSString .
Ein in Java häufig verwendetes Muster ist die Verwendung einer Klasse im flüssigen Stil als Parameter. Beispielsweise:
Dabei wird eine Klasse als Parameter verwendet, und mit einer Klasse im flüssigen Stil wird leicht lesbarer Code erstellt.
Das obige Java-Snippet hilft auch, wenn die Reihenfolge der Parameter nicht so offensichtlich ist. Wir gehen normalerweise bei Koordinaten davon aus, dass X vor Y steht. Und ich sehe normalerweise Höhe vor Breite als Konvention, aber das ist immer noch nicht sehr klar (
something.draw(5, 20)
).Ich habe auch einige Funktionen wie gesehen,
drawWithHeightAndWidth(5, 20)
aber selbst diese können nicht zu viele Parameter annehmen, oder Sie würden anfangen, die Lesbarkeit zu verlieren.quelle
Dimension(int width, int height)
undGridLayout(int rows, int cols)
(die Anzahl der Zeilen ist die Höhe, dhGridLayout
die Höhe zuerst undDimension
die Breite).array_filter($input, $callback)
versusarray_map($callback, $input)
,strpos($haystack, $needle)
versusarray_search($needle, $haystack)
Meistens wird dies durch eine gute Benennung von Funktionen, Parametern und Argumenten gelöst. Sie haben dies jedoch bereits untersucht und festgestellt, dass es Mängel aufweist. Die meisten dieser Mängel werden dadurch behoben, dass Funktionen mit einer geringen Anzahl von Parametern sowohl im aufrufenden als auch im aufgerufenen Kontext klein gehalten werden. Ihr spezielles Beispiel ist problematisch, da die aufgerufene Funktion versucht, mehrere Aufgaben gleichzeitig auszuführen: Festlegen eines Basisrechtecks, Festlegen eines Beschneidungsbereichs, Zeichnen und Füllen mit einer bestimmten Farbe.
Das ist so, als würde man versuchen, einen Satz nur mit den Adjektiven zu schreiben. Fügen Sie dort weitere Verben (Funktionsaufrufe) ein, erstellen Sie ein Subjekt (Objekt) für Ihren Satz und es ist einfacher zu lesen:
Auch wenn
clipRect
undcolor
haben schreckliche Namen (und sie sollten nicht), können Sie immer noch ihre Typen aus dem Kontext unterscheiden.Ihr deserialisiertes Beispiel ist problematisch, weil der aufrufende Kontext versucht, zu viel auf einmal zu tun: etwas zu deserialisieren und zu zeichnen. Sie müssen sinnvolle Namen vergeben und die beiden Verantwortlichkeiten klar voneinander trennen. Zumindest so etwas:
Viele Lesbarkeitsprobleme werden dadurch verursacht, dass versucht wird, zu präzise zu sein und Zwischenschritte übersprungen werden, die Menschen benötigen, um Kontext und Semantik zu erkennen.
quelle
In der Praxis wird dies durch ein besseres Design gelöst. Es ist ausnahmsweise ungewöhnlich, dass gut geschriebene Funktionen mehr als zwei Eingaben annehmen, und wenn dies geschieht, ist es ungewöhnlich, dass diese vielen Eingaben nicht zu einem zusammenhängenden Bündel zusammengefasst werden können. Dies macht es ziemlich einfach, Funktionen oder Aggregatparameter aufzubrechen, damit eine Funktion nicht zu viel bewirkt. Wenn es zwei Eingänge hat, wird es einfach zu benennen und es wird viel klarer, welcher Eingang welcher ist.
Meine Spielzeugsprache hatte das Konzept von Phrasen, um damit umzugehen, und andere, auf natürliche Sprachen fokussierte Programmiersprachen hatten andere Ansätze, um damit umzugehen, aber sie haben alle andere Nachteile. Außerdem sind gerade Phrasen nicht viel mehr als eine nette Syntax dafür, dass Funktionen bessere Namen haben. Es wird immer schwierig sein, einen guten Funktionsnamen zu erstellen, wenn eine Reihe von Eingaben erforderlich sind.
quelle
In Javascript (oder ECMAScript ), zum Beispiel, wuchsen viele Programmierer daran gewöhnt,
Und als Programmierpraxis gelangte es von Programmierern zu ihren Bibliotheken und von dort zu anderen Programmierern, die es immer mehr mochten und benutzten und weitere Bibliotheken usw. schrieben.
Beispiel
Anstatt anzurufen
so was:
Dies ist ein gültiger und korrekter Stil, den Sie als
so was:
, das ist gültig und richtig und nett in Bezug auf Ihre Frage.
Natürlich muss es dafür geeignete Bedingungen geben - in JavaScript ist dies weitaus praktikabler als beispielsweise in C. In JavaScript entstand sogar die mittlerweile weit verbreitete strukturelle Notation, die sich als leichteres Gegenstück zu XML großer Beliebtheit erfreute. Es heißt JSON (Sie haben vielleicht schon davon gehört).
quelle
params
(oft mit optionalen Argumenten und oft selbst optional), wie z . B. diese Funktion . Dies macht Funktionen mit vielen Argumenten ziemlich einfach zu verstehen (in meinem Beispiel gibt es 2 obligatorische und 6 optionale Argumente).Sie sollten dann Objective-C verwenden, hier ist eine Funktionsdefinition:
Und hier wird es verwendet:
Ich denke Ruby hat ähnliche Konstrukte und man kann sie in anderen Sprachen mit Schlüsselwertlisten simulieren.
Für komplexe Funktionen in Java definiere ich gerne Dummy-Variablen im Funktionswortlaut. Für Ihr Links-Rechts-Beispiel:
Sieht nach mehr Code aus, aber Sie können zum Beispiel leftRectangle verwenden und den Code später mit "Lokale Variable extrahieren" umgestalten, wenn Sie der Meinung sind, dass dies für einen zukünftigen Betreuer des Codes, der Sie sein könnte oder nicht, nicht verständlich ist.
quelle
Mein Ansatz ist temporäre lokale Variablen zu schaffen - aber nur um sie nicht nennen
LeftRectange
undRightRectangle
. Ich benutze eher etwas längere Namen, um mehr Bedeutung zu vermitteln. Ich versuche oft, die Namen so weit wie möglich zu unterscheiden, zB nicht beide zu nennensomething_rectangle
, wenn ihre Rolle nicht sehr symmetrisch ist.Beispiel (C ++):
und ich könnte sogar eine einzeilige Wrapper-Funktion oder -Vorlage schreiben:
(Ignorieren Sie das kaufmännische Und, wenn Sie C ++ nicht kennen).
Wenn die Funktion mehrere seltsame Dinge ohne gute Beschreibung ausführt, muss sie wahrscheinlich überarbeitet werden!
quelle