Gibt es einen Grund, die Werte von Parametern, die von value übergeben werden, nicht zu ändern?

9

Gibt es objektive, unterstützbare Argumente für das Software-Engineering für oder gegen das Ändern der Werte von By-Value-Parametern im Hauptteil einer Funktion?

Ein wiederkehrender Spucke (meistens mit viel Spaß) in meinem Team ist, ob die vom Wert übergebenen Parameter geändert werden sollen oder nicht. Einige Mitglieder des Teams sind fest davon überzeugt, dass niemals Parameter zugewiesen werden sollten, damit der ursprünglich an die Funktion übergebene Wert immer abgefragt werden kann. Ich bin anderer Meinung und bin der Meinung, dass Parameter nichts anderes als lokale Variablen sind, die durch die Syntax des Aufrufs der Methode initialisiert werden. Wenn der ursprüngliche Wert eines By-Value-Parameters wichtig ist, kann eine lokale Variable deklariert werden, um diesen Wert explizit zu speichern. Ich bin nicht zuversichtlich, dass einer von uns seine Position sehr gut unterstützt.

Ist dies ein nicht lösbarer religiöser Konflikt oder gibt es gute, objektive Gründe für das Software-Engineering in beide Richtungen?

Hinweis: Die Grundsatzfrage bleibt unabhängig von den Implementierungsdetails der jeweiligen Sprache. In JavaScript beispielsweise, wo die Argumentliste immer dynamisch ist, können Parameter als syntaktischer Zucker für die Initialisierung lokaler Variablen aus dem argumentsObjekt betrachtet werden. Trotzdem könnte man von Parametern deklarierte Bezeichner als "speziell" behandeln, da sie immer noch die Weitergabe von Informationen vom Anrufer an den Angerufenen erfassen.

Joshua Honig
quelle
Es ist immer noch sprachspezifisch; Das Argument, dass es sprachunabhängig ist, weil "es aus Sicht meiner (Lieblingssprache) so ist", ist eine Form ungültigen Arguments. Ein zweiter Grund, warum es sprachspezifisch ist, ist, dass eine Frage, die nicht für alle Sprachen universell beantwortet werden kann, den Kulturen und Normen jeder Sprache unterliegt. In diesem Fall führen unterschiedliche sprachspezifische Normen zu unterschiedlichen Antworten auf diese Frage.
Rwong
Das Prinzip, das Ihre Teammitglieder Ihnen beibringen möchten, ist "eine Variable, ein Zweck". Leider werden Sie so oder so keinen endgültigen Beweis finden. Ich erinnere mich nicht daran, jemals eine Parametervariable für einen anderen Zweck ausgewählt zu haben, und ich erinnere mich auch nicht daran, jemals darüber nachgedacht zu haben. Mir ist nie in den Sinn gekommen, dass es eine gute Idee sein könnte, und ich glaube immer noch nicht, dass es so ist.
Robert Harvey
Ich dachte, das muss schon einmal gefragt worden sein, aber der einzige Betrüger, den ich finden konnte, ist diese ältere SO-Frage .
Doc Brown
3
Es wird als weniger fehleranfällig angesehen, wenn eine Sprache die Mutation von Argumenten verbietet, ebenso wie Designer funktionaler Sprachen die Zuweisung von Aufgaben als weniger fehleranfällig betrachten. Ich bezweifle, dass jemand diese Annahme anhand einer Studie bewiesen hat.
Frank Hileman

Antworten:

15

Ich bin anderer Meinung und bin der Meinung, dass Parameter nichts anderes als lokale Variablen sind, die durch die Syntax des Aufrufs der Methode initialisiert werden

Ich nehme eine dritte Position ein: Parameter sind wie lokale Variablen: Beide sollten standardmäßig als unveränderlich behandelt werden. Variablen werden also einmal zugewiesen und dann nur gelesen, nicht geändert. Bei Schleifen ist for each (x in ...)die Variable im Kontext jeder Iteration unveränderlich. Die Gründe dafür sind:

  1. es macht den Code einfacher, "in meinem Kopf auszuführen",
  2. Es ermöglicht aussagekräftigere Namen für die Variablen.

Angesichts einer Methode mit einer Reihe von Variablen, die zugewiesen werden und sich dann nicht ändern, kann ich mich auf das Lesen des Codes konzentrieren, anstatt zu versuchen, den aktuellen Wert jeder dieser Variablen zu speichern.

Angesichts der gleichen Methode, diesmal mit weniger Variablen, die sich jedoch ständig ändern, muss ich mich jetzt an den aktuellen Status dieser Variablen erinnern und daran arbeiten, was der Code tut.

Nach meiner Erfahrung ist Ersteres für mein armes Gehirn weitaus einfacher. Andere, klügere Leute als ich haben dieses Problem vielleicht nicht, aber es hilft mir.

Was den anderen Punkt betrifft:

double Foo(double r)
{
    r = r * r;
    r = Math.Pi * r;
    return r;
}

gegen

double Foo(int r)
{
    var rSquared = r * r;
    var area = Math.Pi * rSquared;
    return area;
} 

Erfundene Beispiele, aber meiner Meinung nach ist es aufgrund der Variablennamen viel klarer, was im zweiten Beispiel vor sich geht: Sie vermitteln dem Leser weit mehr Informationen als diese Masse rim ersten Beispiel.

David Arno
quelle
"Lokale Variablen ... sollten standardmäßig als unveränderlich behandelt werden." - Was?
Whatsisname
1
Es ist ziemlich schwierig, forSchleifen mit unveränderlichen Variablen auszuführen . Das Einkapseln der Variablen in die Funktion reicht Ihnen nicht aus? Wenn Sie Probleme haben, Ihren Variablen in einer bloßen Funktion zu folgen, sind Ihre Funktionen möglicherweise zu lang.
Robert Harvey
@RobertHarvey for (const auto thing : things)ist eine for-Schleife, und die eingeführte Variable ist (lokal) unveränderlich. for i, thing in enumerate(things):mutiert auch keine Einheimischen
Caleth
3
Dies ist meiner Meinung nach im Allgemeinen ein sehr gutes mentales Modell. Eines möchte ich jedoch hinzufügen: Oft ist es akzeptabel, eine Variable in mehr als einem Schritt zu initialisieren und sie anschließend als unveränderlich zu behandeln. Dies ist nicht gegen die hier vorgestellte allgemeine Strategie, sondern dagegen, dies immer wörtlich zu befolgen.
Doc Brown
3
Wow, wenn ich diese Kommentare lese, kann ich sehen, dass viele Leute hier offensichtlich das funktionale Paradigma nie studiert haben.
Joshua Jones
4

Ist dies ein nicht lösbarer religiöser Konflikt oder gibt es gute, objektive Gründe für das Software-Engineering in beide Richtungen?

Das ist eine Sache, die ich an (gut geschriebenem) C ++ wirklich mag: Sie können sehen, was Sie erwartet. const string& x1ist eine konstante Referenz, string x2ist eine Kopie und const string x3ist eine konstante Kopie. Ich weiß, was in der Funktion passieren wird. x2 wird geändert, denn wenn es nicht wäre, gäbe es keinen Grund, es nicht konstant zu machen.

Wenn Sie also eine Sprache haben, die dies zulässt , erklären Sie, was Sie mit den Parametern tun möchten. Das sollte die beste Wahl für alle Beteiligten sein.

Wenn Ihre Sprache dies nicht zulässt, gibt es leider keine Silberkugel-Lösung. Beides ist möglich. Die einzige Richtlinie ist das Prinzip der geringsten Überraschung. Nehmen Sie einen Ansatz und gehen Sie damit durch Ihre gesamte Codebasis, mischen Sie ihn nicht.

nvoigt
quelle
Eine andere gute Sache ist das int f(const T x)und sie unterscheiden int f(T x)sich nur im Funktionskörper.
Deduplikator
3
Eine C ++ - Funktion mit einem Parameter wie const int xnur um x klar zu machen, wird im Inneren nicht geändert? Sieht für mich nach einem sehr seltsamen Stil aus.
Doc Brown
@DocBrown Ich beurteile keinen der beiden Stile. Ich sage nur, dass jemand, der der Meinung ist, dass Parameter nicht geändert werden sollten, den Compiler dazu bringen kann, dies zu garantieren, und jemand, der der Meinung ist, dass eine Änderung in Ordnung ist, dies auch tun kann. Beide Absichten können durch die Sprache selbst klar kommuniziert werden. Dies ist ein großer Vorteil gegenüber einer Sprache, die dies nicht garantieren kann und bei der Sie sich auf willkürliche Richtlinien verlassen müssen und hoffen, dass jeder sie liest und sich an sie hält.
nvoigt
@nvoigt: Wenn jemand einen Funktionsparameter ("nach Wert") ändern möchte, würde er ihn einfach constaus der Parameterliste entfernen, wenn er dies behindert. Ich denke also nicht, dass dies die Frage beantwortet.
Doc Brown
@DocBrown Dann ist es nicht mehr const. Es ist nicht wichtig, ob es sich um eine Konstante handelt oder nicht. Wichtig ist, dass die Sprache eine Möglichkeit enthält , dies dem Entwickler ohne Zweifel mitzuteilen . Meine Antwort ist, dass es keine Rolle spielt, solange Sie es klar kommunizieren , was C ++ einfach macht, andere möglicherweise nicht und Sie Konventionen benötigen.
nvoigt
1

Ja, es ist ein "religiöser Konflikt", außer dass Gott keine Programme liest (soweit ich weiß), also wird er zu einem Lesbarkeitskonflikt herabgestuft, der eine Frage des persönlichen Urteils wird. Und ich habe es auf jeden Fall getan und gesehen, in beide Richtungen. Beispielsweise,

void propagate (OBJECT * -Objekt, double t0, double dt) {
  doppeltes t = t0; / * lokale Kopie von t0, um eine Änderung des Werts zu vermeiden * /
  während (was auch immer) {
    timestep_the_object (...);
    t + = dt; }}
  Rückkehr; }}

Nur wegen des Argumentnamens t0 würde ich mich hier wahrscheinlich dafür entscheiden, seinen Wert nicht zu ändern. Nehmen wir stattdessen an, wir haben diesen Argumentnamen lediglich in tNow oder so ähnlich geändert . Dann hatte ich höchstwahrscheinlich eine lokale Kopie vergessen und einfach tNow + = dt; für diese letzte Aussage.

Angenommen, ich wusste, dass der Client, für den das Programm geschrieben wurde, einige neue Programmierer mit sehr wenig Erfahrung einstellen wird, die diesen Code lesen und daran arbeiten würden. Dann würde ich mir wahrscheinlich Sorgen machen, sie mit Pass-by-Value versus -reference zu verwechseln, während ihre Gedanken zu 100% damit beschäftigt sind, die gesamte Geschäftslogik zu verdauen. In einem solchen Fall würde ich immer eine lokale Kopie eines Arguments deklarieren, das geändert wird, unabhängig davon, ob es für mich mehr oder weniger lesbar ist oder nicht.

Antworten auf diese Art von Stilfragen tauchen mit Erfahrung auf und haben selten eine kanonische religiöse Antwort. Jeder, der glaubt, es gäbe nur eine Antwort (und dass er es weiß :), braucht wahrscheinlich mehr Erfahrung.

John Forkosh
quelle