Benannte Argumente (Parameter) als Lesbarkeitshilfe

11

Vor langer Zeit habe ich viel in ADA programmiert, und es war normal, beim Aufrufen einer Funktion Argumente zu benennen - SomeObject.DoSomething (SomeParameterName => someValue);

Jetzt, da C # benannte Argumente unterstützt, denke ich darüber nach, in Situationen, in denen es möglicherweise nicht offensichtlich ist, was ein Argument bedeutet, zu dieser Gewohnheit zurückzukehren.

Sie könnten argumentieren, dass es immer offensichtlich sein sollte, was ein Argument bedeutet, aber wenn Sie ein boolesches Argument haben und Anrufer "true" oder "false" übergeben, macht das Qualifizieren des Werts mit dem Namen die Aufrufsite lesbarer.

contentFetcher.DownloadNote (Hinweis, Handbuch: true);

Ich denke, ich könnte Enums erstellen, anstatt true oder false zu verwenden (manuell, in diesem Fall automatisch).

Was halten Sie davon, gelegentlich benannte Argumente zu verwenden, um das Lesen von Code zu erleichtern?

Damian
quelle
Ein Parameterkommentar über den Methoden hilft ebenfalls.
Amir Rezaei
1
@ amir-rezaei: Parameterkommentare über Methoden helfen nur, wenn Sie den Kommentaren vertrauen können. Wenn Sie keine guten Entwickler und gute Codeüberprüfungsprozesse haben, würde ich den Kommentaren nicht vertrauen.
Btilly
Als einmaliger Ada-Benutzer bin ich für den gelegentlichen Gebrauch, wie Sie beschreiben. So erinnere ich mich, dass sie in Ada verwendet wurden, übrigens - ich würde es nicht "abnormal" nennen, aber das Wort "normal" scheint zu implizieren, dass die meisten Aufrufe Parameternamen angeben würden, so wie ich mich nicht an Dinge erinnere. Natürlich variieren die Konventionen, aber unnötiges Durcheinander ist in jeder IMO-Sprache eine schlechte Konvention.
Steve314
Ich bin damit einverstanden, dass Sie Ada verwenden - es war nicht etwas, was wir die ganze Zeit getan haben, aber wo es geholfen hat.
Damian

Antworten:

7

Dies wurde in der Entwicklung von C ++ vorgeschlagen, und Stroustrup erörtert dies in seinem "Design and Evolution of C ++", Seite 153 und folgende. Der Vorschlag war wohlgeformt und stützte sich auf frühere Erfahrungen mit Ada. Es wurde nicht adoptiert.

Der Hauptgrund war, dass niemand Funktionen mit einer großen Anzahl von Parametern fördern wollte. Jede zusätzliche Funktion in einer Sprache kostet etwas, und es bestand kein Wunsch, eine Funktion hinzuzufügen, um das Schreiben fehlerhafter Programme zu vereinfachen.

Es wurden auch Fragen zu den kanonischen Parameternamen aufgeworfen, insbesondere in der üblichen Konvention für Header und Codedateien. Einige Organisationen hatten längere und aussagekräftigere Parameternamen in der .h-Datei und kürzere und einfacher einzugebende Namen in der .cpp-Datei (Ersatzdateisuffixe nach Wunsch). Das Erfordernis, dass diese identisch sind, würde zusätzliche Kosten für die Kompilierung verursachen, und das Verwechseln von Namen zwischen Quelldateien könnte subtile Fehler verursachen.

Es kann auch mithilfe von Objekten anstelle von Funktionsaufrufen behandelt werden. Erstellen Sie anstelle eines GetWindow-Aufrufs mit einem Dutzend Parametern eine Window-Klasse mit einem Dutzend privater Variablen und fügen Sie bei Bedarf Setter hinzu. Durch Verketten der Setter ist es möglich, so etwas wie zu schreiben my_window.SetColor(green).SetBorder(true).SetBorderSize(3);. Es ist auch möglich, verschiedene Funktionen mit unterschiedlichen Standardeinstellungen zu haben, die die Funktion aufrufen, die die Arbeit tatsächlich erledigt.

Wenn Sie sich nur Sorgen über den Dokumentationseffekt von machen contentFetcher.DownloadNote(note, manual : true);, können Sie immer so etwas schreiben contentFetcher.DownloadNote(note, /* manual */ true);, sodass es in der Dokumentation nicht einmal sehr hilfreich ist.

David Thornley
quelle
Lustigerweise begann ich, als ich von Ada nach C wechselte, die von Ihnen beschriebene Konvention zu verwenden - Code-Reviewer hassten sie jedoch. Stimmen Sie zu, es nicht für eine große Anzahl von Parametern zu verwenden.
Damian
7

Ich denke, hier geht es darum, schlechten Code besser lesbar zu machen, anstatt um „Best Practice“.

Eine Methode (oder ein Auftragnehmer) mit 20 Parametern ist ein „schlechter Geruch“ und wahrscheinlich auf ein Problem in Ihrem Design zurückzuführen. Wenn ich jedoch gezwungen bin, an Code zu arbeiten, wenn Methoden viele Parameter annehmen, machen benannte Parameter den Code weniger schwer verständlich.

Wenn Methoden nur 1 oder 2 Parameter haben und aus dem Methodennamen ersichtlich ist, um welche Parameter es sich handelt, fügt der benannte Parameter nichts hinzu. Dies ist der ideale Fall.

Wenn der gesamte Code, an dem Sie arbeiten, als Teil des Buches „ Clean Code “ geschrieben ist, haben Sie nur sehr wenig Verwendung für benannte Parameter, wir leben jedoch in der realen Welt.

Ian
quelle
1
Eine Funktion mit zwei Parametern kann verwirrend sein, wenn beide Parameter vom gleichen Typ sind und keine Ahnung haben, welcher welcher ist. Natürlich können Sie die Funktion so benennen, dass sie diesen Hinweis liefert, aber Sie tun wirklich nur das, was die Parameternamen tun - außer dass Sie es obligatorisch machen, diese Ausführlichkeit auch dann aufzunehmen, wenn der Kontext (z. B. die Ausdrücke, die das liefern) Parameter) machen deutlich, welcher Parameter welcher ist.
Steve314
1
@ Steve314 Beispiel:void TrackDataChange(Data oldData, Data newData)
Alexander
3

Ich bin damit einverstanden, dass das Hinzufügen des Parameternamens die Lesbarkeit verbessert. Die meisten Bücher, die ich gelesen habe, scheinen jedoch boolesche Schalter als schlechte Praxis zu betrachten. Ich mache das manchmal:

public Content DownloadNote(Note note)
{
    return downloadNote(note, manual: false);
}

public Content DownloadNoteManually(Note note)
{
    return downloadNote(note, manual: true);
}

Dies gibt Ihnen mehr Flexibilität bei der Implementierung Ihrer API. Sie können damit auch den Fall steuern, in dem Sie mehrere boolesche Schalter haben, aber nicht alle gleichzeitig aktiv sein können.

Scott Whitlock
quelle
3
Wenn Sie x Auswahlmöglichkeiten haben, werden Sie 2 ^ x Frontends erstellen?
Apoorv020
@ apoorv020 - Wenn ich genug Parameter für einen Methodenaufruf hätte, bei dem 2 ^ x signifikant wurde, würde ich eine neue Klasse erstellen, die die Parameterwerte enthält, und nur einen Parameter übergeben.
Scott Whitlock
@ scott-whitlock: Option 1 - Erstellen Sie ein Objekt, legen Sie x Eigenschaften fest und rufen Sie eine Methode einmal mit Ihrem Objekt auf. Option 2 - Rufen Sie eine Methode mit x benannten Parametern auf. Was besser ist, hängt von Ihrer Sprache ab. In einer dynamisch typisierten Sprache erfordert Option 1 deutlich mehr Kesselplatte ohne Verstärkung und ist daher schlechter. In einer statisch typisierten Sprache fügen Sie immer noch das Boilerplate hinzu, aber Tests auf falsch benannte Schlüssel werden eher zur Kompilierungszeit als zur Laufzeit durchgeführt. Daher ist Option 1 in C # besser, aber Option 2 ist in Python ein klarer Gewinn.
Btilly
@btilly - Wie Ian betonte, weist Clean Code eindeutig darauf hin, dass Sie keine Funktionen mit mehr als 2 oder 3 Parametern haben sollen. Um fair zu sein, handelte es sich um Java, das statisch typisiert ist. Das OP fragt auch nach C #, das statisch typisiert ist. Ich würde immer noch lieber Funktionsüberladungen und / oder andere genau benannte Funktionsnamen als eine lange Liste von Parametern verwenden.
Scott Whitlock
@ Scott-Whitlock: Sind wir uns eigentlich nicht einig? Wir sind uns einig, was in C # am besten ist. Wir wissen beide, was Clean Code sagt. Mein Punkt war, dass es wichtig ist zu verstehen, warum es gut ist, damit Sie wissen, wann der Rat in eine andere Umgebung passt oder nicht.
Btilly
3

Ich glaube fest an benannte Parameter in Situationen, in denen die Typen und die Semantik aus dem Namen der Methode nicht ersichtlich sind. Ich habe die Erfahrung gemacht, dass nur wenige Leute die Dokumentation lesen.

Allerdings sollten benannte Parameter keine Alternative zu sinnvollen Argumentlisten, der Verwendung von Hilfsobjekten (um semantisch verwandte Argumente zu "verknüpfen") und gegebenenfalls der Verwendung von Aufzählungen sein.

Uri
quelle
0

Ich denke, dies ist nützlicher in Nicht-OO-Sprachen, in denen Sie möglicherweise eine Funktion haben, die auf verschiedene, etwas unterschiedliche Arten etwas tun muss, und die Art und Weise, wie sie bestimmt, was zu tun ist, auf Parameterwerten basiert. In der OOP-Welt würden wir die Funktion nur überladen, aber wenn dies nicht möglich ist, werden Sie eine Reihe von Flags übergeben (oder eine Reihe von Werten, und ob sie übergeben werden oder nicht, ist das Flag).

Ich denke, es ist bis zu einem gewissen Grad besser lesbar. Aber wie andere bereits erwähnt haben, ist es ein Code-Geruch, viele Parameter zu haben. Daher sehe ich in einer objektorientierten Sprache wie C # keine große Verwendung dafür.

TMN
quelle