Techniken zum Minimieren der Anzahl von Funktionsargumenten

13

In Clean Code heißt es, dass "die ideale Anzahl von Argumenten für eine Funktion Null ist". Die Gründe dafür werden erklärt und sind sinnvoll. Was ich suche, sind Techniken zur Umgestaltung von Methoden mit 4 oder mehr Argumenten, um dieses Problem zu lösen.

Eine Möglichkeit ist, die Argumente in eine neue Klasse zu extrahieren, aber das würde sicherlich zu einer Explosion von Klassen führen? Und diese Klassen werden wahrscheinlich Namen haben, die gegen einige der Namensregeln verstoßen (die mit "Data" oder "Info" usw. enden)?

Eine andere Methode besteht darin, Variablen, die von mehreren Funktionen verwendet werden, zu privaten Membervariablen zu machen, um deren Übergabe zu vermeiden. Dadurch wird der Gültigkeitsbereich der Variablen jedoch möglicherweise so erweitert, dass sie für Funktionen offen ist, die sie nicht wirklich benötigen.

Ich suche nur nach Möglichkeiten, Funktionsargumente zu minimieren, nachdem ich die Gründe akzeptiert habe, warum es eine gute Idee ist, dies zu tun.

Neil Barnwell
quelle
21
Tbh Ich bin überhaupt nicht mit sauberem Code einverstanden. Wenn die Anzahl der Argumente für eine Funktion Null ist, bedeutet dies, dass die Funktion Nebenwirkungen hat und wahrscheinlich irgendwo den Status ändert. Ich stimme zwar zu, dass weniger als 4 Argumente eine gute Faustregel sein können - ich hätte lieber eine Funktion mit 8 Argumenten, die statisch ist und keine Nebenwirkungen hat, als eine nicht statische Funktion mit null Argumenten, die den Zustand ändert und Nebenwirkungen hat .
Wasatz
4
" In Clean Code steht geschrieben, dass" die ideale Anzahl von Argumenten für eine Funktion Null ist ". " Wirklich? Das ist so falsch! Die ideale Anzahl von Parametern ist eins, wobei ein Rückgabewert deterministisch von diesem einen Parameter abgeleitet wird. In der Praxis spielt die Anzahl der Parameter jedoch keine große Rolle. Es kommt darauf an, dass die Funktion, wann immer möglich, rein ist (dh ihren Rückgabewert leitet sie nur aus ihren Parametern ohne Nebenwirkungen ab).
David Arno
2
Nun, das Buch wird später darauf hinweisen, dass Nebenwirkungen nicht wünschenswert sind ...
Neil Barnwell
2
Mögliche Duplikate von Strategien für das Umbrechen
Mücke

Antworten:

16

Das Wichtigste, woran Sie sich erinnern sollten, ist, dass dies Richtlinien und keine Regeln sind.

Es gibt Fälle, in denen eine Methode einfach ein Argument annehmen muss . Denken Sie an die+ Methode für Zahlen. Oder die addMethode für eine Sammlung.

In der Tat könnte argumentieren , man sogar das , was es bedeutet, zwei Zahlen zu addieren auf Kontext, zum Beispiel in z abhängig ist 3 + 3 == 6, aber in z | 5 3 + 3 == 2 , so sollte wirklich der Additionsoperator ein Verfahren auf einem Kontextobjekt sein , das dauert zwei Argumente anstelle einem Methode für Zahlen mit einem Argument.

Ebenso muss eine Methode zum Vergleichen von zwei Objekten entweder eine Methode sein, bei der ein Objekt das andere als Argument verwendet, oder eine Methode des Kontexts, bei der zwei Objekte als Argumente verwendet werden. Daher ist es einfach nicht sinnvoll, eine Vergleichsmethode mit zu haben weniger als ein Argument.

Es gibt jedoch einige Möglichkeiten, um die Anzahl der Argumente für eine Methode zu verringern:

  • Verkleinern Sie die Methode selbst : Wenn die Methode so viele Argumente benötigt, tut sie vielleicht zu viel?
  • Eine fehlende Abstraktion : Wenn die Argumente eng miteinander korrelieren, gehören sie möglicherweise zusammen, und es fehlt eine Abstraktion? (Beispiel für ein kanonisches Lehrbuch: PointÜbergeben Sie anstelle von zwei Koordinaten ein Objekt, oder übergeben Sie anstelle von Benutzername und E-Mail ein IdCardObjekt.)
  • Objektstatus : Wenn das Argument von mehreren Methoden benötigt wird, sollte es möglicherweise Teil des Objektstatus sein. Wenn es nur von einigen Methoden, aber nicht von anderen benötigt wird, tut das Objekt möglicherweise zu viel und sollte eigentlich zwei Objekte sein.

Eine Möglichkeit ist, die Argumente in eine neue Klasse zu extrahieren, aber das würde sicherlich zu einer Explosion von Klassen führen?

Wenn Ihr Domain-Modell viele verschiedene Arten von Dingen enthält, hat Ihr Code am Ende viele verschiedene Arten von Objekten. Daran ist nichts auszusetzen.

Und diese Klassen werden wahrscheinlich Namen haben, die gegen einige der Namensregeln verstoßen (die mit "Data" oder "Info" usw. enden)?

Wenn Sie keinen richtigen Namen finden, haben Sie möglicherweise zu viele oder zu wenige Argumente gruppiert. Sie haben also entweder nur ein Fragment einer Klasse oder Sie haben mehr als eine Klasse.

Eine andere Methode besteht darin, Variablen, die von mehreren Funktionen verwendet werden, zu privaten Membervariablen zu machen, um deren Übergabe zu vermeiden. Dadurch wird der Gültigkeitsbereich der Variablen jedoch möglicherweise so erweitert, dass sie für Funktionen offen ist, die sie nicht wirklich benötigen.

Wenn Sie eine Gruppe von Methoden haben, die alle mit denselben Argumenten arbeiten, und eine andere Gruppe von Methoden, die dies nicht tun, gehören sie möglicherweise zu verschiedenen Klassen.

Beachten Sie, wie oft ich das Wort "vielleicht" verwendet habe. Deshalb sind das Richtlinien, keine Regeln. Vielleicht ist Ihre Methode mit 4 Parametern völlig in Ordnung!

Jörg W. Mittag
quelle
7
@ BrunoSchäpper: Klar: (1) " Die Methode selbst verkleinern: Wenn die Methode so viele Argumente benötigt, macht sie vielleicht zu viel? ". Die Anzahl der Params ist ein schlechter Test dafür. Optionale / boolesche Parameter und viele Codezeilen sind starke Indikatoren dafür, dass eine Methode zu viel tut. Viele Params sind bestenfalls schwach. (2) " Objektstatus: Wenn das Argument von mehreren Methoden benötigt wird, sollte es möglicherweise Teil des Objektstatus sein ". Nein, nein und dreimal, nein. Objektstatus minimieren; keine Funktionsparameter. Wenn möglich, übergeben Sie einen Wert über Parameter an alle Methoden, um den Objektstatus zu vermeiden.
David Arno
Das Beispiel, das Sie für die Hinzufügung angegeben haben, ist absolut falsch. Die addFunktion für natürliche Zahlen und die addFunktion für den Ring von ganzen Zahlen mod n sind zwei verschiedene Funktionsaktionen für zwei verschiedene Typen. Ich verstehe auch nicht, was Sie unter "Kontext" verstehen.
Gardenhead
Thx @DavidArno. 1) vereinbart, kein starker Indikator für sich. Aber trotzdem eine gute. Ich sehe oft ein paar Methoden mit ein paar herumgereichten Objekten. Es gibt keinen Objektzustand. Das ist gut, aber 2) bessere Option IMHO überarbeitet diese Methoden, verschiebt den impliziten Status in eine neue Klasse, die alle diese Parameter als explizite Argumente verwendet. Am Ende stehen Ihnen eine öffentliche Nullargumentmethode und viele interne Null-zu-Eins-Argumentmethoden zur Verfügung. Staat ist nicht öffentlich, global oder wird sogar lange am Leben gehalten, aber Code ist viel sauberer.
Bruno Schäpper
6

Beachten Sie, dass Null-Argumente keine Nebenwirkungen implizieren, da Ihr Objekt ein implizites Argument ist. Schauen Sie sich zum Beispiel an, wie viele Null-Aritäts-Methoden die unveränderliche Liste von Scala enthält .

Eine nützliche Technik, die ich als "Linsenfokussierung" bezeichne. Wenn Sie ein Kameraobjektiv fokussieren, ist es einfacher, den wahren Fokuspunkt zu erkennen, wenn Sie zu weit gehen, und ihn dann wieder an den richtigen Punkt zu verschieben. Gleiches gilt für Software-Refactoring.

Vor allem, wenn Sie die verteilte Versionskontrolle verwenden, können Sie leicht mit Softwareänderungen experimentieren, um zu sehen, ob Sie sie mögen, und um sich zurückzuziehen, wenn Sie dies nicht tun.

Im Zusammenhang mit Ihrer aktuellen Frage bedeutet dies, dass Sie die Null- oder Ein-Argument-Versionen mit mehreren abgespaltenen Funktionen zuerst schreiben und dann relativ leicht erkennen, welche Funktionen zur besseren Lesbarkeit kombiniert werden müssen.

Beachten Sie, dass der Autor auch ein großer Befürworter der testgetriebenen Entwicklung ist, die zu Beginn tendenziell Funktionen mit geringer Arität hervorbringt, da Sie mit Ihren einfachen Testfällen beginnen.

Karl Bielefeldt
quelle
1
Wie bei Ihrer Analogie zum Fokussieren von Objektiven - Insbesondere beim Refactoring ist es wichtig, das Weitwinkelobjektiv anstelle des Nahbereichsobjektivs zu verwenden. Und in # der Parameter zu schauen ist einfach zu nah
Tofro
0

Ein Ansatz, der einfach (und naiv - oder sollte ich auch blind sagen ) nur darauf abzielt, die Anzahl der Argumente für Funktionen zu reduzieren, ist wahrscheinlich falsch. Es ist absolut nichts falsch an Funktionen mit einer großen Anzahl von Argumenten. Wenn sie von der Logik benötigt werden, sind sie auch erforderlich ... Eine lange Parameterliste macht mir überhaupt keine Sorgen - solange sie richtig formatiert und aus Gründen der Lesbarkeit kommentiert ist.

In dem Fall, dass alle oder eine Teilmenge von Argumenten zu einer eindeutigen logischen Entität gehören und üblicherweise in Gruppen innerhalb Ihres Programms weitergegeben werden, ist es möglicherweise sinnvoll, sie in einem Container zu gruppieren - in der Regel einer Struktur oder einem anderen Objekt. Typische Beispiele sind Nachrichten oder Ereignisdatentypen .

Sie können diesen Ansatz leicht übertreiben. Wenn Sie feststellen, dass das Ein- und Auspacken von Gegenständen in und aus solchen Transportbehältern mehr Aufwand verursacht als die Lesbarkeit verbessert, sind Sie wahrscheinlich zu weit gegangen.

Große Parameterlisten können ein Zeichen dafür sein, dass Ihr Programm möglicherweise nicht richtig strukturiert ist. Möglicherweise versucht die Funktion, die eine so große Anzahl von Parametern erfordert, zu viel und sollte in mehrere kleinere Funktionen aufgeteilt werden. Ich möchte lieber hier anfangen, als mir Gedanken über die Anzahl der Parameter zu machen.

Tofro
quelle
5
Es ist natürlich falsch, die Anzahl der Argumente blind zu reduzieren. Aber ich bin anderer Meinung als "Es ist absolut nichts falsch an Funktionen mit einer großen Anzahl von Argumenten." . Wenn Sie eine Funktion mit einer großen Anzahl von Argumenten treffen, gibt es meiner Meinung nach in 99,9% aller Fälle eine Möglichkeit, die Struktur des Codes gezielt zu verbessern, wodurch (auch) die Anzahl der Argumente der Funktion verringert wird.
Doc Brown
@ DocBrown Deshalb gibt es diesen letzten Absatz und die Empfehlung, dort anzufangen .... Und noch einen: Sie haben wahrscheinlich noch nie versucht, gegen eine MS Windows-API zu programmieren;)
Tofro
"Vielleicht versucht die Funktion, die eine so große Anzahl von Parametern erfordert, zu viel und sollte in mehrere kleinere Funktionen aufgeteilt werden." Ich stimme vollkommen zu, obwohl Sie in der Praxis nicht einfach eine andere Funktion haben, die höher im Stapel liegt und diese mehreren kleineren Funktionen aufruft? Sie könnten sie dann in ein Objekt umgestalten, aber dieses Objekt hat einen ctor. Sie könnten einen Baumeister benutzen, bla bla bla. Der Punkt ist, dass es sich um eine unendliche Regression handelt - irgendwo gibt es eine Reihe von Werten, die für die Software benötigt werden, um ihre Aufgabe zu erfüllen, und sie müssen irgendwie zu diesen Funktionen gelangen.
Neil Barnwell
1
@NeilBarnwell Im Idealfall (Refactoring-Wert) können Sie eine Funktion, für die 10 Argumente erforderlich sind, in drei Funktionen aufteilen, für die jeweils 3-4 Argumente erforderlich sind. Im nicht so idealen Fall stehen Ihnen drei Funktionen zur Verfügung, für die jeweils 10 Argumente erforderlich sind (lassen Sie es dann besser in Ruhe). In Bezug auf Ihr übergeordnetes Stapelargument: Stimmen Sie zu - Möglicherweise, aber nicht unbedingt - Argumente kommen von irgendwoher, und der Abruf kann auch irgendwo im Stapel erfolgen und muss nur an der richtigen Stelle dort abgelegt werden - Kilometerstand neigt dazu zu variieren.
Tofro
Die Softwarelogik benötigt nie mehr als vier Parameter. Möglicherweise nur ein Compiler.
Doktor
0

Es gibt keinen magischen Weg: Sie müssen die Problemdomäne beherrschen, um die richtige Architektur zu finden. Das ist der einzige Weg zur Umgestaltung: die Beherrschung der Problemdomäne. Mehr als vier Parameter sind nur eine sichere Wette, dass Ihre aktuelle Architektur fehlerhaft und falsch ist.

Die einzigen Ausnahmen sind Compiler (Metaprogramme) und Simulationen, bei denen die Grenze theoretisch 8, aber wahrscheinlich nur 5 beträgt.

Der Doktor
quelle