it-swarm.dev

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

Manchmal (selten) scheint es der beste Weg zu sein, eine Funktion zu erstellen, die eine angemessene Anzahl von Parametern benötigt. Wenn ich das tue, habe ich jedoch das Gefühl, dass ich die Reihenfolge der Parameter oft zufällig wähle. Normalerweise gehe ich nach "Reihenfolge der Wichtigkeit", wobei der wichtigste Parameter zuerst kommt.

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

55
Casey Patton

Im Allgemeinen: benutze es.

Schreiben Sie einen Test für Ihre Funktion, einen realen Test.
Etwas das möchten Sie eigentlich mit dieser Funktion machen.

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

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

zB Nehmen alle ein Dokument/Objekt/Dateizeiger/Wertereihe/Koordinaten als erstes Argument (s)? Um Gottes willen entsprechen diesen Argumenten.

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

55
ZJR

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

Auswahltrichter

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

1. Semantik zuerst

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

  • fühle mich natürlich anzurufen,
  • in Bezug auf Absicht und Verhalten selbstbeschreibend sein.

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

2. Dann Wichtigkeit

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

3. Dann Frequenz

Die Häufigkeit spielt ebenfalls eine Rolle, insbesondere in einer Sprache, in der Sie keine benannten Parameter haben, aber Standardwerte für Positionsparameter haben können. Dies bedeutet, dass die Reihenfolge der Parameter nicht variiert 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 verfügt über ein Konzept eines Platzhalterparameters ).

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

4. Vergessen wir nicht die E/A.

wenn Ihre Methode/Funktion eine Eingabe nimmt und eine Ausgabe erzeugt und diese nicht "zurückgegeben" (über eine return-Anweisung) oder "ausgelöst" (unter Verwendung eines Ausnahmesystems) werden soll, bleibt Ihnen die Option zum Übergeben Werte zurück an den Anrufer unter Verwendung Ihrer anderen Parameter (oder des Eingabeparameters). Dies 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, wäre die Verwendung eines funktionalen Ansatzes oder die Definition eines Builder-Musters , damit Sie Ihre Eingaben klar stapeln, Ihre Ausgaben definieren und abrufen können wenn nötig.

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


Einige Dinge zu beachten

  • Benutzerfreundlichkeit

    Die meisten der oben genannten Punkte werden sich natürlich zeigen, wenn Sie ZJRs Rat : folgen. Verwenden Sie es!

  • Refactoring in Betracht ziehen

    Wenn Sie sich Gedanken über die Reihenfolge der Parameter machen, liegt diese Sorge möglicherweise oben und in der Tatsache, dass Ihre API schlecht gestaltet ist. Wenn Sie zu viele Parameter haben, kann höchstwahrscheinlich etwas komponiert/modularisiert und überarbeitet werden .

  • Leistung berücksichtigen

    Beachten Sie, dass die Implementierungen einiger Sprachen bei der Verwendung von Parametern sehr wichtige Auswirkungen auf Ihre Laufzeitspeicherverwaltung haben. Daher der Grund, warum die Stilbücher vieler Sprachen empfehlen, die Parameterliste einfach und kurz zu halten . Zum Beispiel bei maximal 4 Parametern. Ich überlasse es Ihnen als Übung, herauszufinden, warum.

  • Bevans Antwort und Erwähnung von Clean Code 's Empfehlungen sind definitiv auch relevant!

28
haylem

Ich würde respektvoll behaupten, dass die Sorge um die Reihenfolge der Parameter die Sorge um das Falsche ist.

In Onkel Bobs Buch " Clean Code " befürwortet er überzeugend, 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.

Wie unvollkommen ich auch sein mag, ich versuche, dem Rat von Onkel Bob zu folgen - und es verbessert meinen Code.

In den seltenen Fällen, in denen eine Methode mehr Informationen zu benötigen scheint , ist die Einführung eines Parameterobjekts eine gute Idee. Normalerweise ist dies der erste Schritt zur Entdeckung eines neuen Konzepts (Objekts), das für meinen Algorithmus von entscheidender Bedeutung ist.

20
Bevan

Ich versuche, zuerst die IN-Parameter und dann die OUT-Parameter zu setzen. Es gibt auch einige natürliche Ordnungen, z. createPoint(double x, double y) ist createPoint(double y, double x) stark vorzuziehen.

10
quant_dev

Ich habe noch nie eine dokumentierte "Best Practice" zu diesem bestimmten Thema gesehen, aber mein persönlicher Standard besteht darin, 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 Datenbankschema oder in den Datenschichtmethoden erscheinen würden.

Wenn es mehrere Überladungen einer Methode gibt, stelle ich fest, dass die typische Methode darin besteht, sie mit Parametern aufzulisten, die allen (oder den meisten) Methoden gemeinsam sind, wobei für jede Methodenüberladung wie jede andere Methode an das 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) { }
6
Joel Etherton

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

6
J.K.

Ich folge oft der C/C++ - Konvention, bei der zuerst die Parameter const (dh die Parameter, die Sie als Wert übergeben) und dann die Parameter, die Sie als Referenz übergeben, platziert werden. Dies ist möglicherweise nicht unbedingt 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 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

3
Bill

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

Die Verwendung mehrerer Parameter ist häufig ein klarer Indikator dafür, dass Sie bei dieser Methode gegen SRP verstoßen. Es ist unwahrscheinlich, dass eine Methode, die viele Parameter benötigt, nur eine Sache ausführt. 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 heilige Wasser meidet. Je mehr Parameter Sie innerhalb einer Methode verwenden, desto höher ist die Wahrscheinlichkeit, dass die Methode (zu) komplex ist. Je komplexer, desto schwieriger zu warten und weniger wünschenswert.

Wenn ich das tue, habe ich jedoch das Gefühl, dass ich die Reihenfolge der Parameter oft zufällig wähle. Normalerweise gehe ich nach "Reihenfolge der Wichtigkeit", wobei der wichtigste Parameter zuerst kommt.

Im Prinzip wählen Sie zufällig . Natürlich könnte man denken, dass paramter [~ # ~] 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 Reihenfolge aufmerksam waren - für andere könnte es zufällig erscheinen.

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

Es gibt mehrere Auswege:

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 ausgewählt haben. Dies ist Nice 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önnen ein Dictionary/Hashmap/Associative Array als Parameter verwenden: z. Javascript würde Folgendes zulassen: fn({"name":"John Doe", age:36}), das 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, aber Sie würden Typinformationen verlieren (z. B. wenn Sie mit HashMap<String, Object> arbeiten), wenn Parameter unterschiedliche Typen haben (und müssen) Besetzung).

Der nächste logische Schritt wäre, ein Object (wenn Sie Java verwenden) mit geeigneten Eigenschaften oder etwas Leichterem wie einer Struktur zu übergeben. (wenn Sie zB 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

1
Thomas Junk

Normalerweise gehe ich einfach mit der Parameterreihenfolge "Was sieht weniger zyprisch aus" vor. Je seltener ich zur Methoden-/Funktionsdefinition gehen muss, desto besser. Und es ist schön, benannte Parameter zu haben, die beschreiben, wofür sie verwendet werden. Wenn der kleine Tooltip (VS) angezeigt wird, wird dies noch einfacher.

Wenn Sie Linien und Linien von Parametern haben, möchten Sie möglicherweise ein anderes Design in Betracht ziehen. Treten Sie zurück und sehen Sie, wie Sie dies 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.

1
user29981

Oft ist ein komplexes Objekt als Parameter besser - eine 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, sollten Sie versuchen, die Standardbibliotheksdokumentation von PHP zu lesen.

0
Wyatt Barnett

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

Wenn Sie das aufrufende Objekt (häufig als Absender bezeichnet) übergeben, wird dies zuerst ausgeführt.

Die Liste sollte etwas fließend enthalten, was bedeutet, dass Sie wissen sollten, worum es bei einem Argument geht, wenn Sie es lesen. Beispiel:

CopyFolder (Zeichenfolgenpfad, Bool rekursiv);

Wenn Sie zuerst rekursiv setzen würden, wäre dies verwirrend, da es noch keinen Kontext gibt. Wenn es bereits jetzt darum geht, einen Ordner (2) zu kopieren (1), macht das rekursive Argument Sinn.

Dadurch funktionieren auch IntelliSense-ähnliche Funktionen gut: Der Benutzer lernt, wenn er die Argumente ausfüllt, und baut ein Verständnis für die Methode und ihre Funktionsweise auf. Ebenso sollten Überlastungen für die gleichen Teile die gleiche Reihenfolge beibehalten.

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 Strings hintereinander und dann ein paar Boolesche Werte besser aussehen. Auf einer bestimmten Ebene wird dies eher eine Kunst als eine Fähigkeit.

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

0
Martin Maat

Normalerweise bestelle ich sie zuerst nach Bedarf, dann nach einem kombinierten Maß für Wichtigkeit und Verwendungshäufigkeit nach einem "Gefühl" (kann als ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency) angesehen werden) und nicht nach einer bestimmten Praxis.

Wie andere angemerkt haben, denke ich, dass das zugrunde liegende Problem, das dies zu einem Problem macht, darin besteht, zu viele Parameter zu verwenden (IMHO, alles> 3 ist zu viele), und das ist das eigentliche Problem, das Sie angehen sollten. Es gibt einen interessanten Beitrag darüber in Rebecca Murpheys Blog.

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

0
shesek

Ähnlich wie bei der Antwort von @Wyatt Barnetts, mehr als ein paar Parameter oder sehr explizite Parameter für die Methode, würde ich empfehlen, stattdessen ein Objekt zu übergeben. Dies ist in der Regel einfacher zu aktualisieren/zu warten, übersichtlicher zu lesen und beseitigt die Notwendigkeit, sich um die Bestellung zu kümmern . Außerdem sind zu viele Parameter für eine Methode ein Codegeruch und es gibt häufig Refactoring-Muster , denen Sie folgen 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 einfach mit.

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...

0
longda

Bestellen Sie es auf die Art und Weise, von der Sie glauben, dass Currys höchstwahrscheinlich profitieren wird. Zum Beispiel zuerst Funktionsparameter.

0
alternative