Was ist die beste Vorgehensweise zum Bestellen von Parametern in einer Funktion?

52

Manchmal (selten) scheint es der beste Weg zu sein, eine Funktion zu erstellen, die eine angemessene Menge von Parametern benötigt. Allerdings habe ich das Gefühl, dass ich die Reihenfolge der Parameter oft nach dem Zufallsprinzip wähle. Normalerweise gehe ich nach "Reihenfolge der Wichtigkeit" vor, wobei der wichtigste Parameter an erster Stelle steht.

Gibt es einen besseren Weg, dies zu tun? Gibt es eine "Best Practice" -Methode zum Ordnen von Parametern, die die Übersichtlichkeit erhöht?

Casey Patton
quelle
5
Benannte Parameter machen dies weniger als ein Problem. Python bringt es auf das Äußerste, schauen Sie hinein. Ein gutes Beispiel ist in C # - die vielen Möglichkeiten zum Aufrufen MessageBox.Show. Schauen Sie sich das auch an.
Job
8
In Haskell deutet die mögliche Nützlichkeit der Anwendung von Teilfunktionen normalerweise auf eine natürliche Reihenfolge der Argumente hin.
tdammers
Was würdest du für eine anständige Menge an Parametern halten?
Chris
Ich dachte> 2. Obwohl ich vermute, dass die Reihenfolge für 2 Parameter gleich ist.
Casey Patton
3
@tdammers das gilt auch für jede sprache, die direkte unterstützung für currying hat
jk.

Antworten:

55

Im Allgemeinen: benutze es .

Schreiben Sie einen Test für Ihre Funktion, einen realen Test.
Etwas, das Sie gerne mit dieser Funktion machen würden .

Und sehen Sie, in welcher Reihenfolge Sie diese abgelegt haben.

Es sei denn, Sie haben bereits einige Funktionen (oder kennen diese), die etwas Ähnliches tun.
In diesem Fall: Passen Sie sich an, was sie bereits tun, zumindest für die ersten Argumente.

Nehmen sie zB alle ein Dokument / Objekt / Dateizeiger / Wertereihen / Koordinaten als erstes Argument? Um Gottes willen, folge diesen Argumenten .

Vermeiden Sie es , Ihre Mitarbeiter und Ihr zukünftiges Selbst zu verwechseln .

ZJR
quelle
12
"Vermeiden Sie es, ... Ihr zukünftiges Ich zu verwirren" klingt im Allgemeinen nach einer guten Philosophie.
Jeanne Pindar
28

Ich gehe normalerweise nach diesen Regeln vor, aber nicht immer mit der gleichen Priorität. Ich denke, es ist jetzt ein automatischer Denkprozess, und ich überlege es nicht, außer für das öffentliche API-Design .

Auswahltrichter

  1. Semantik
  2. Bedeutung / Relevanz
  3. Häufigkeit der Nutzung
  4. I / O-Bedenken

1. Semantik zuerst

Wählen Sie insbesondere in OOP Parameter basierend auf ihrer semantischen Bedeutung für die Aktion oder Nachricht aus. Die Signatur einer benannten Methode mit einem benannten Parameter sollte:

  • fühlen sie sich natürlich anzurufen,
  • in Bezug auf Absicht und Verhalten selbstbeschreibend sein.

(Aus diesen Gründen kann manchmal die Verwendung benutzerdefinierter Typen oder Aliase anstelle von Grundelementen die Ausdruckskraft Ihrer Signatur erhöhen.)

2. Dann Wichtigkeit

Der "signifikanteste" Parameter kommt zuerst (oder als nächstes ...)

3. Dann Frequenz

Die Häufigkeit spielt auch eine Rolle, insbesondere in einer Sprache, in der Sie keine benannten Parameter haben, aber Standardwerte für Positionsparameter haben können. Dies bedeutet, dass sich die Reihenfolge der Parameter nicht ändert und dass Sie die N + 1-Parameter offensichtlich nicht festlegen können, wenn Sie den Standardwert des N-ten Parameters erzwingen möchten (es sei denn, Ihre Sprache hat das Konzept eines Platzhalterparameters ).

Die gute Nachricht für Sie ist, dass sich die Häufigkeit in der Regel auf die Wichtigkeit bezieht, sodass dies mit dem vorherigen Punkt einhergeht. Und dann liegt es wahrscheinlich an Ihnen, Ihre API so zu gestalten, dass sie die entsprechende Semantik aufweist.

4. Lassen Sie uns I / O nicht vergessen

Wenn Ihre Methode / Funktion eine Eingabe annimmt und eine Ausgabe erzeugt und letztere nicht "zurückgegeben" (über eine return-Anweisung) oder "geworfen" (unter Verwendung eines Ausnahmesystems) werden soll, haben Sie die Möglichkeit zu übergeben Werte werden mit Ihren anderen Parametern (oder dem Eingabeparameter) an den Aufrufer zurückgegeben. Das bezieht sich auf die Semantik, und in den meisten Fällen ist es sinnvoll, dass die ersten Parameter die Ausgabe definieren und die letzten Parameter die Ausgabe empfangen.

Ein anderer Ansatz, um weniger Parameter zu haben und die Semantik zu maximieren, ist die Verwendung eines funktionalen Ansatzes oder die Definition eines Builder-Musters , damit Sie Ihre Eingaben übersichtlich stapeln, Ihre Ausgaben definieren und sie bei Bedarf abrufen können.

(Beachten Sie, dass ich keine globalen Variablen erwähne, denn warum sollten Sie eine verwenden, richtig?)


Einige Dinge zu beachten

  • Benutzerfreundlichkeit

    Die meisten der oben genannten Punkte werden natürlich angezeigt, wenn Sie den Ratschlägen von ZJR folgen : Use It!

  • Betrachten Sie Refactoring

    Wenn Sie sich Gedanken über die Parameterreihenfolge machen, liegt dies möglicherweise an der oben genannten Ursache und an der Tatsache, dass Ihre API schlecht entworfen wurde. Wenn Sie zu viele Parameter haben, kann höchstwahrscheinlich etwas komponiert / modularisiert und umgestaltet werden .

  • Betrachten Sie die Leistung

    Beachten Sie, dass die Implementierung einiger Sprachen bei der Verwendung von Parametern sehr wichtige Auswirkungen auf die Laufzeitspeicherverwaltung hat. Daher empfehlen die Style-Books vieler Sprachen, die Parameterliste einfach und kurz zu halten . Zum Beispiel bei maximal 4 Parametern. Ich überlasse es Ihnen, herauszufinden, warum.

  • Bevans Antwort und Erwähnung der Empfehlungen von Clean Code sind definitiv ebenfalls relevant!

Haylem
quelle
18

Ich würde respektvoll einreichen, dass die Sorge um die Parameterreihenfolge die Sorge um das Falsche ist.

In Onkel Bobs Buch " Clean Code " tritt er überzeugend dafür ein, dass Methoden niemals mehr als zwei Argumente haben sollten - und die meisten sollten nur eines haben, wenn überhaupt. In diesem Fall ist die Bestellung entweder offensichtlich oder unwichtig.

Trotzdem versuche ich unvollkommen, Onkel Bobs Rat zu befolgen - und es verbessert meinen Code.

In den seltenen Fällen, in denen eine Methode mehr Informationen zu erfordern scheint, ist die Einführung eines Parameterobjekts eine gute Idee. Normalerweise ist dies der erste Schritt zur Entdeckung eines neuen Konzepts (Objekts), das der Schlüssel zu meinem Algorithmus ist.

Bevan
quelle
Ich stimme dir definitiv zu! Meine erste Aussage zielte darauf ab, herauszufinden, dass dies keine typische Sache für mich ist, haha. Aber falls ich feststelle, dass ich wirklich einige Parameter übergeben muss und es sich nicht lohnt, das gesamte Programm umzugestalten, frage ich mich, ob ich etwas bestellen soll.
Casey Patton
3
Husten PHP Husten . Es tut mir leid - Sie haben etwas darüber gesagt, dass Sie sich nicht um die Reihenfolge der Parameter sorgen müssen?
Craige
Haben Sie dann nicht einfach einen Konstruktor für Ihr Parameterobjekt, der genauso viele Argumente akzeptiert?
Detly
Nein - weil das Parameterobjekt über mehrere Anweisungen besser lesbar "aufgebaut" werden kann.
Bevan
2
@Bevan: das ist sehr umstritten. Wenn Sie Objekte auf diese Weise aufbauen, können Sie nicht mehr unveränderlich sein. Die Unveränderlichkeit Ihrer Objekte, wann immer dies möglich ist, ist eine weitere Möglichkeit, die Wartbarkeit zu verbessern.
Tdammers
10

Ich versuche, die IN-Parameter an erster Stelle, OUT-Parameter an zweiter Stelle zu setzen. Es gibt auch einige natürliche Ordnungen, die zB createPoint(double x, double y)stark vorzuziehen sind createPoint(double y, double x).

quant_dev
quelle
5
Manchmal ist das Gegenteil besser, z. B. wenn es immer nur ein OUT-Argument und eine variable Anzahl von in-Argumenten gibt, wie in addAllTo(target, something1, something2).
Maaartinus
Obwohl ich der gleichen Regel folge, versuche ich, OUT-Parameter und die meisten guten Programmierer nach Möglichkeit zu meiden. Daher sollte die Anzahl der Funktionen, bei denen diese Empfehlung dem OP tatsächlich hilft, eher gering sein.
Doc Brown
7
@ DocBrown Sie sollten wirklich nicht versuchen, gute Programmierer zu meiden, sie können nützlich sein, um herum zu haben.
RyanfaeScotland
@ RyanfaeScotland: Verdammt, ich sollte meine Kommentare zweimal lesen, bevor ich sie poste (oder zumindest innerhalb der fünf Minuten, in denen ich sie ändern kann) ;-)
Doc Brown
6

Ich habe noch nie eine dokumentierte "Best Practice" in Bezug auf dieses spezielle Thema gesehen, aber mein persönlicher Standard ist es, sie entweder in der Reihenfolge aufzulisten, in der sie in der Methode erscheinen, für die sie verwendet werden, oder wenn die Methode eher eine ist Durchgang zu einer Datenschicht Ich werde sie in der Reihenfolge auflisten, in der sie im DB-Schema oder in den Datenschichtmethoden erscheinen würden.

Wenn es mehrere Überladungen einer Methode gibt, stelle ich fest, dass die typische Methode darin besteht, sie beginnend mit Parametern aufzulisten, die allen (oder den meisten) Methoden gemeinsam sind, wobei für jede Methodenüberladung wie jede andere Methode am Ende angehängt wird :

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }
Joel Etherton
quelle
6

Reihenfolge: Eingabe (n), Ausgabe (n), optionale Parameter.

Jonathan Khoo
quelle
3

Ich folge oft der C / C ++ - Konvention, dass die constParameter zuerst platziert werden ( dh die Parameter, die Sie als Wert übergeben) und dann die, die Sie als Referenz übergeben. Dies ist möglicherweise nicht die richtige Methode zum Aufrufen von Funktionen. Wenn Sie jedoch daran interessiert sind, wie jeder Compiler mit Parametern umgeht, finden Sie unter den folgenden Links die Regeln und / oder die Reihenfolge, in der die Parameter auf den Stapel verschoben werden.

http://msdn.microsoft.com/en-us/library/zthk2dkh%28v=vs.80%29.aspx

http://en.wikipedia.org/wiki/Calling_convention

Rechnung
quelle
Ein weiterer Link, der Sie möglicherweise interessiert, ist msdn.microsoft.com/en-us/library/984x0h58%28v=vs.71%29.aspx. Informationen zu rekursiven Funktionen finden Sie unter folgendem Link. publications.gbdirect.co.uk/c_book/chapter4/… Nicht dass ich es vergessen hätte, aber als neuer Benutzer kann ich keinen Kommentar veröffentlichen, der mehr als zwei Links enthält .... wie frustrierend!
Bill
1

Normalerweise gehe ich einfach mit der Parameterreihenfolge "Was weniger zyprisch aussieht" um. Je weniger Male ich zur Methoden- / Funktionsdefinition gehen muss, desto besser. Und es ist schön, benannte Parameter zu haben, die beschreiben, wofür sie verwendet werden. Auf diese Weise wird es noch einfacher, wenn der kleine Tooltip (VS) angezeigt wird.

Wenn Sie Linien und Parameterlinien haben, möchten Sie möglicherweise ein anderes Design in Betracht ziehen. Machen Sie einen Schritt zurück und sehen Sie, wie Sie das in weitere Funktionen / Methoden aufteilen können. Nur eine Idee, aber wenn ich ein Dutzend Parameter in meiner Funktion habe, ist es fast immer kein Parameterproblem, sondern ein Designproblem.


quelle
2
"cyprtic": eine großartige Möglichkeit, den Punkt zu machen.
Bevan
2
Du hattest noch nie einen Tippfehler, @Bevan?
1
Ich habe die ganze Zeit Tippfehler - das Schreiben von cyprtic war so gut, dass ich dachte, es sei absichtlich . Bitte ändern Sie es zurück, es hilft, um Ihren Standpunkt zu verdeutlichen, auch wenn es ein Unfall war.
Bevan
1
@Bevan, haha ​​okay, ich verstehe, was du sagst. Guter Punkt! Es hat sich wieder geändert =)
1

Manchmal (selten) scheint es der beste Weg zu sein, eine Funktion zu erstellen, die eine angemessene Menge von Parametern benötigt.

Die Verwendung mehrerer Parameter ist häufig ein eindeutiger Hinweis darauf, dass Sie bei dieser Methode gegen die SRP verstoßen . Es ist unwahrscheinlich, dass eine Methode, die viele Parameter benötigt, nur eines tut. Excpetion kann eine mathematische Funktion oder eine Konfigurationsmethode sein, bei der tatsächlich mehrere Parameter als solche benötigt werden. Ich würde mehrere Parameter vermeiden, da der Teufel das Weihwasser meidet. Je mehr Parameter Sie in einer Methode verwenden, desto höher ist die Wahrscheinlichkeit, dass die Methode (zu) komplex ist. Je komplexer, desto schwieriger zu warten und desto weniger wünschenswert.

Allerdings habe ich das Gefühl, dass ich die Reihenfolge der Parameter oft nach dem Zufallsprinzip wähle. Normalerweise gehe ich nach "Reihenfolge der Wichtigkeit" vor, wobei der wichtigste Parameter an erster Stelle steht.

Im Prinzip wählen Sie nach dem Zufallsprinzip . Natürlich könnte man denken, dass Parameter A relevanter ist als Parameter B ; Dies ist jedoch möglicherweise nicht der Fall für Benutzer Ihrer API, die B für den relevantesten Parameter halten. Selbst wenn Sie bei der Auswahl der Bestellung aufpassen, kann dies für andere zufällig erscheinen .

Gibt es einen besseren Weg, dies zu tun? Gibt es eine "Best Practice" -Methode zum Ordnen von Parametern, die die Übersichtlichkeit erhöht?

Es gibt verschiedene Möglichkeiten:

a) Der triviale Fall: Verwenden Sie nicht mehr als einen Parameter.

b) Da Sie nicht angegeben haben, welche Sprache Sie gewählt haben, besteht die Möglichkeit, dass Sie eine Sprache mit benannten Parametern gewählt haben . Dies ist ein schöner syntaktischer Zucker , mit dem Sie die Bedeutung der Reihenfolge der Parameter lockern können:fn(name:"John Doe", age:36)

Nicht jede Sprache erlaubt solche Feinheiten. Und was dann?

c) Sie könnten ein Dictionary / Hashmap / Associative Array als Parameter verwenden: zB würde Javascript folgendes erlauben: fn({"name":"John Doe", age:36})welches nicht weit von (b) entfernt ist.

d) Natürlich, wenn Sie mit einer statisch typisierten Sprache wie Java arbeiten. Sie könnten eine Hashmap verwenden , verlieren jedoch die Typinformationen (z. B. beim Arbeiten mit HashMap<String, Object>), wenn Parameter unterschiedliche Typen haben (und umgewandelt werden müssen).

Der nächste logische Schritt wäre die Übergabe eines Object(wenn Sie Java verwenden) mit entsprechenden Eigenschaften oder etwas Leichterem wie einer Struktur (wenn Sie z. B. C # oder C / C ++ schreiben).

Faustregel:

1) Bester Fall - Ihre Methode benötigt überhaupt keinen Parameter

2) Guter Fall - Ihre Methode benötigt einen Parameter

3) Tolerierbarer Fall - Ihre Methode benötigt zwei Parameter

4) Alle anderen Fälle sollten überarbeitet werden

Thomas Junk
quelle
0

Oft ist ein komplexes Objekt als Parameter besser - die Version der benannten Parameter eines armen Mannes, die auf den meisten Plattformen funktioniert. Und öffnet die Tür zu Parametern mit Verhalten zum Booten.

Als Beispiel für die meisten Dinge, die Sie nicht tun sollten, möchten Sie vielleicht die Standard-Bibliotheksdokumentation von PHP lesen.

Wyatt Barnett
quelle
0

Ich bestelle sie in der Regel zuerst mit der erforderlichen, dann nach einem kombinierten Maß an Wichtigkeit und Verwendungshäufigkeit nach einem "Gefühl" (kann als "Gefühl" angesehen werden ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency)) und nicht nach einer bestimmten Praxis.

Wie andere angemerkt haben, verwendet das zugrunde liegende Problem, das dies zu einem Problem macht, zu viele Parameter (IMHO, alles> 3 ist zu viele), und das ist das eigentliche Problem, mit dem Sie sich befassen sollten. Es gibt einen interessanten Beitrag dazu im Blog von Rebecca Murphey.

Ich denke, wenn Sie nur 1-3 Argumente haben, ist die richtige Reihenfolge ziemlich offensichtlich und Sie "fühlen" nur, was richtig ist.

shesek
quelle
0

Ähnlich wie bei der Antwort von @Wyatt Barnetts würde ich empfehlen, stattdessen ein Objekt zu übergeben, das nicht nur einige Parameter oder sehr explizite Parameter für die Methode enthält. Dies ist in der Regel einfacher zu aktualisieren / warten, übersichtlicher zu lesen und macht das Bestellen überflüssig . Außerdem sind zu viele Parameter für eine Methode ein Codegeruch, und es gibt gängige Refactoring-Muster, die Sie befolgen können, um sie zu korrigieren.

Explizites Beispiel:

public int add(int left, int right)
{
  return left + right;
}

Da dies ein ziemlich klar definiertes Beispiel ist und die Addition kommutativ ist (Reihenfolge spielt keine Rolle), machen Sie es einfach.

Wenn Sie jedoch etwas Komplexeres hinzufügen:

public SomeComplexReturnValue Calculate(int i, float f, string s, object o)
{
  // do work here
}

Würde werden:

public class SomeComplexInputForCalculation
{
  public int i; 
  public float f;
  public string s; 
  public object o;
}

public SomeComplexReturnValue Calculate(SomeComplexInputForCalculation input)
{
  // do work here
}

Hoffe das hilft...

longda
quelle
0

Bestellen Sie es so, wie es Ihnen am ehesten gefällt. Zum Beispiel zuerst die Funktionsparameter.

Alternative
quelle
0

"Wichtig zuerst" bedeutet nicht viel. Es gibt einige Konventionen.

Wenn Sie das aufrufende Objekt (häufig Absender genannt) übergeben, geht das zuerst.

Die Liste sollte fließend sein , dh Sie sollten wissen, worum es bei einem Argument geht, wenn Sie es lesen. Beispiel:

CopyFolder (Zeichenkettenpfad, Bool rekursiv);

Wenn Sie zuerst rekursiv stellen, ist dies verwirrend, da noch kein Kontext vorhanden ist. Wenn es Ihnen jetzt schon darum geht, einen Ordner (1) zu kopieren (2), dann fängt das Argument rekursiv an Sinn zu machen.

Dadurch funktionieren auch IntelliSense-ähnliche Funktionen gut: Der Benutzer lernt, während er die Argumente eingibt, und baut ein Verständnis für die Methode und ihre Funktionsweise auf. Ebenso sollten Überladungen die gleiche Reihenfolge für die Teile beibehalten, die gleich sind.

Dann finden Sie möglicherweise immer noch Argumente, die in dieser Hinsicht "gleich" sind, und Sie könnten versucht sein, die Reihenfolge von der Art des Arguments abhängen zu lassen. Nur weil ein paar Saiten hintereinander und dann ein paar Boolesche besser aussehen. Auf einer gewissen Ebene wird dies eher eine Kunst als eine Fähigkeit.

Die Aussage, dass Sie alle Argumente in ein Objekt einschließen sollten, um nur ein einziges Argument zu erhalten, interessiert mich nicht sonderlich. Das täuscht sich nur selbst (es macht die Methode nicht weniger komplex, es hat immer noch all diese Argumente, man hat sie einfach versteckt). Dies kann immer noch praktisch sein, wenn Sie die Menge ein paarmal von Methode zu Methode weitergeben. Das Refactoring wird sicher viel einfacher, aber in Bezug auf das Design wird nur so getan, als ob Sie einen Unterschied machen. Es ist nicht so, dass Sie am Ende ein aussagekräftiges Objekt haben, das etwas darstellt, sondern nur eine Reihe von Argumenten, die sich nicht von der Liste in der Methodendeklaration unterscheiden. Es wird Onkel Bob nicht glücklich machen.

Martin Maat
quelle