2010-03-12 9 views
69

Gelegentlich müssen wir Methoden schreiben, die viele viele Argumente erhalten, zum Beispiel:Best Practice für die Übergabe vieler Argumente an die Methode?

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2) 
{ 
} 

Wenn ich diese Art von Problem auftreten sollte, habe ich oft Argumente in eine Karte kapseln.

Map<Object,Object> params = new HashMap<Object,Object>(); 
params.put("objA",ObjA) ; 

...... 

public void doSomething(Map<Object,Object> params) 
{ 
// extracting params 
Object objA = (Object)params.get("objA"); 
...... 
} 

Dies ist keine gute Praxis, kapseln Params in eine Karte ist total eine Verschwendung von Effizienz. Die gute Sache ist, die saubere Unterschrift, einfach, andere Parameter mit weniger Änderung hinzuzufügen. Was ist die beste Vorgehensweise für diese Art von Problem?

Antwort

101

In Effective Java, Kapitel 7 (Methoden), Artikel 40 (Designmethodensignaturen sorgfältig), Bloch schreibt:

drei Techniken für übermäßig lange Parameterlisten Es gibt eine Verkürzung:

  • Pause die Methode in mehrere Methoden, die jeweils nur eine Teilmenge der Parameter benötigen
  • Hilfsklassen erstellen, die eine Gruppe von Parametern enthalten (normalerweise statische Elementklassen)
  • Anpassen des Builder-Musters von Objektkonstrukt Methode aufrufen.

Für weitere Details, ermutige ich Sie, das Buch zu kaufen, es ist es wirklich wert.

+0

Was ist "überlange Parameter"? Wann können wir sagen, dass eine Methode zu viele Parameter hat?Gibt es eine bestimmte Nummer oder einen Bereich? –

+0

@RedM Ich habe immer mehr als 3 oder 4 Parameter als "übermäßig lang" betrachtet – jtate

+1

@jtate ist das eine persönliche Entscheidung oder folgst du einem offiziellen Dokument? –

2

Eine gute Übung wäre Refactoring. Was ist mit diesen Objekten bedeutet, dass sie an diese Methode weitergegeben werden sollten? Sollten sie in ein einzelnes Objekt eingekapselt sein?

+0

ja sollten sie. Zum Beispiel hat ein großes Suchformular viele nicht zusammenhängende Einschränkungen und Anforderungen für die Paginierung. Sie müssen pass currentPageNumber, searchCriteria, pageSize ... – Sawyer

4

Sie könnten eine Klasse erstellen, die diese Daten enthält. Muss zwar sinnvoll sein, aber viel besser als eine Karte (OMG).

+0

Ich glaube nicht, dass es notwendig ist, eine Klasse zu erstellen, um einen Methodenparameter zu halten. – Sawyer

+0

Ich würde die Klasse nur erstellen, wenn mehrere Instanzen die gleichen Parameter übergeben würden. Dies würde signalisieren, dass die Parameter zusammenhängen und wahrscheinlich zusammengehören. Wenn Sie eine Klasse für eine einzelne Methode erstellen, ist die Heilung wahrscheinlich schlimmer als die Krankheit. – tvanfosson

+0

Ja - Sie könnten verwandte Parameter in ein DTO oder Wertobjekt verschieben. Sind einige der mehreren Parameter optional, d. H. Die Hauptmethode ist mit diesen zusätzlichen Parametern überladen? In solchen Fällen - ich denke, es ist akzeptabel. – JoseK

61

Eine Karte mit magischen String-Schlüsseln ist eine schlechte Idee. Sie verlieren die Überprüfung der Kompilierzeit, und es ist wirklich unklar, was die erforderlichen Parameter sind. Sie müssten sehr vollständige Dokumentation schreiben, um das nachzuholen. Werden Sie sich in ein paar Wochen daran erinnern, was diese Strings sind, ohne sich den Code anzusehen? Was ist, wenn Sie einen Tippfehler gemacht haben? Verwenden Sie den falschen Typ? Sie werden es erst herausfinden, wenn Sie den Code ausgeführt haben.

Verwenden Sie stattdessen ein Modell. Machen Sie eine Klasse, die ein Container für all diese Parameter sein wird. Auf diese Weise behalten Sie die Typsicherheit von Java. Sie können dieses Objekt auch an andere Methoden übergeben, es in Sammlungen usw. platzieren.

Natürlich, wenn der Parametersatz nicht an anderer Stelle verwendet oder weitergegeben wird, kann ein dediziertes Modell übertrieben sein. Es muss ein Gleichgewicht gefunden werden, also benutze den gesunden Menschenverstand.

2

Die Verwendung einer Karte ist eine einfache Möglichkeit, die Anrufsignatur zu bereinigen, aber dann haben Sie ein anderes Problem. Sie müssen in den Hauptteil der Methode schauen, um zu sehen, was die Methode in dieser Map erwartet, welche Schlüsselnamen oder welche Typen die Werte haben.

Ein saubererer Weg wäre, alle Parameter in einer Objekt-Bean zu gruppieren, aber das Problem wird immer noch nicht vollständig behoben.

Was Sie hier haben, ist ein Design-Problem. Bei mehr als 7 Parametern für eine Methode werden Sie Probleme haben, sich daran zu erinnern, was sie repräsentieren und welche Reihenfolge sie haben. Von hier aus erhalten Sie viele Fehler, indem Sie die Methode in falscher Parameterreihenfolge aufrufen.

Sie brauchen ein besseres Design der App nicht eine bewährte Methode, um viele Parameter zu senden.

5

Es gibt ein Muster namens Parameter object.

Idee ist, ein Objekt anstelle aller Parameter zu verwenden. Auch wenn Sie später Parameter hinzufügen müssen, müssen Sie sie nur zum Objekt hinzufügen. Die Methodenschnittstelle bleibt gleich.

10

Zuerst würde ich versuchen, die Methode zu refaktorieren. Wenn es so viele Parameter verwendet, kann es auf irgendeine Weise zu lang sein. Durch das Aufteilen würde sowohl der Code verbessert als auch die Anzahl der Parameter für jede Methode reduziert. Sie können möglicherweise auch die gesamte Operation in eine eigene Klasse umgestalten. Zweitens würde ich nach anderen Instanzen suchen, in denen ich dieselbe (oder Obermenge) der gleichen Parameterliste verwende. Wenn Sie mehrere Instanzen haben, signalisiert dies wahrscheinlich, dass diese Eigenschaften zusammengehören. In diesem Fall erstellen Sie eine Klasse, die die Parameter enthält und verwendet.Zuletzt würde ich bewerten, ob es die Anzahl der Parameter wert ist, ein Kartenobjekt zu erzeugen, um die Lesbarkeit des Codes zu verbessern. Ich denke, das ist ein persönlicher Anruf - mit dieser Lösung gibt es Schmerzen, und der Punkt, an dem die Abwägung stattfindet, kann sich unterscheiden. Für sechs Parameter würde ich es wahrscheinlich nicht tun. Für 10 würde ich wahrscheinlich (wenn keine der anderen Methoden zuerst funktioniert).

12

Es heißt "Einführung Parameter-Objekt". Wenn Sie feststellen, dass Sie an mehreren Stellen die gleiche Parameterliste übergeben, erstellen Sie einfach eine Klasse, in der sie alle enthalten sind.

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2); 
// ... 
doSomething(param); 

Auch wenn Sie nicht selbst finden so oft gleiche Parameterliste vorbei, so einfach Refactoring und trotzdem wird die Lesbarkeit des Codes verbessern, die immer gut. Wenn Sie sich 3 Monate später Ihren Code ansehen, ist es leichter zu verstehen, wann Sie einen Fehler beheben oder ein Feature hinzufügen müssen.

Es ist natürlich eine allgemeine Philosophie, und da Sie keine Details angegeben haben, kann ich Ihnen auch keinen detaillierteren Rat geben. :-)

+0

wird die Müllsammlung ein Problem sein? – rupinderjeet

+0

Nicht, wenn Sie das Parameterobjekt in der Aufruferfunktion als lokalen Bereich definieren und wenn Sie es nicht mutieren. Es wird höchstwahrscheinlich gesammelt und sein Gedächtnis unter solchen Umständen ziemlich schnell wiederverwendet. – dimitarvp

+0

Imo, Sie sollten auch einen 'XXXParameter param = new XXXParameter();' verfügbar haben und dann 'XXXParameter.setObjA (objA)'; etc ... – satibel

1

Erstellen Sie eine Bean-Klasse und legen Sie alle Parameter fest (Setter-Methode) und übergeben Sie dieses Bean-Objekt an die Methode.

0

Wenn Sie zu viele Parameter übergeben, versuchen Sie, die Methode zu refaktorieren. Vielleicht macht es eine Menge Dinge, die es nicht tun soll. Wenn das nicht der Fall ist, ersetzen Sie die Parameter durch eine einzelne Klasse. Auf diese Weise können Sie alles in einer einzelnen Klasseninstanz kapseln und die Instanz übergeben und nicht die Parameter.

21

Wenn Sie viele optionale Parameter haben, können Sie fließend API erstellen: einzelne Methode mit der Kette von Methoden

exportWithParams().datesBetween(date1,date2) 
        .format("xml") 
        .columns("id","name","phone") 
        .table("angry_robots") 
        .invoke(); 

Die Verwendung von statischen Import ersetzen können Sie innere fließend APIs erstellen:

... .datesBetween(from(date1).to(date2)) ... 
+2

+1 für die ziemlich kreative Idee, muss ich zugeben! –

+1

Was ist, wenn alle Parameter benötigt werden, nicht optional? – Emerald214

+1

Sie können auf diese Weise auch Standardparameter festlegen. Auch das [builder pattern] (http://en.wikipedia.org/wiki/Builder_pattern) bezieht sich auf fließende Interfaces. Das sollte wirklich die Antwort sein, denke ich. Abgesehen davon, dass ein langer Konstruktor in kleinere Initialisierungsmethoden zerlegt wird, die optional sind. –

0
  • Sehen Sie sich Ihren Code an und sehen Sie, warum all diese Parameter übergeben werden. Manchmal ist es möglich, die Methode selbst zu refaktorieren.

  • Die Verwendung einer Karte macht Ihre Methode anfällig. Was passiert, wenn jemand, der Ihre Methode verwendet, einen Parameternamen falsch schreibt oder eine Zeichenfolge postet, bei der Ihre Methode eine UDT erwartet?

  • Definieren Sie eine Transfer Object. Es wird Ihnen zumindest eine Typüberprüfung geben; Es ist sogar möglich, dass Sie eine Validierung am Ort der Verwendung statt innerhalb Ihrer Methode durchführen.

3

Code Complete * schlägt vor, ein paar Dinge:

  • „Beschränken Sie die Anzahl der Parameter einer Routine auf etwa sieben Sieben ist eine magische Zahl für Menschen Verständnis.“ (S. 108).
  • "Setzen Sie die Parameter in die Reihenfolge Eingabe-Ändern-Ausgabe ... Wenn mehrere Routinen ähnliche Parameter verwenden, geben Sie die ähnlichen Parameter in einer konsistenten Reihenfolge ein" (S. 105).
  • Zuletzt Status oder Fehlervariablen setzen.
  • Als tvanfosson erwähnt, übergeben Sie nur die Teile einer strukturierten Variablen (Objekte), die die Routine benötigt. Das heißt, wenn Sie die meisten der strukturierten Variablen in der Funktion verwenden, übergeben Sie einfach die gesamte Struktur, aber beachten Sie, dass dies die Kopplung zu einem gewissen Grad fördert.

* Erste Ausgabe, ich weiß, ich sollte aktualisieren. Es ist auch wahrscheinlich, dass sich einige dieser Ratschläge geändert haben, seit die zweite Ausgabe geschrieben wurde, als OOP anfing, populärer zu werden.

5

Dies ist oft ein Problem beim Konstruieren von Objekten.

In diesem Fall verwenden Sie Builder Objektmuster, es funktioniert gut, wenn Sie große Liste von Parametern haben und nicht immer alle von ihnen benötigen.

Sie können es auch an den Methodenaufruf anpassen.

Es erhöht auch die Lesbarkeit sehr.

public class BigObject 
{ 
    // public getters 
    // private setters 

    public static class Buider 
    { 
    private A f1; 
    private B f2; 
    private C f3; 
    private D f4; 
    private E f5; 

    public Buider setField1(A f1) { this.f1 = f1; return this; } 
    public Buider setField2(B f2) { this.f2 = f2; return this; } 
    public Buider setField3(C f3) { this.f3 = f3; return this; } 
    public Buider setField4(D f4) { this.f4 = f4; return this; } 
    public Buider setField5(E f5) { this.f5 = f5; return this; } 

    public BigObject build() 
    { 
     BigObject result = new BigObject(); 
     result.setField1(f1); 
     result.setField2(f2); 
     result.setField3(f3); 
     result.setField4(f4); 
     result.setField5(f5); 
     return result; 
    } 
    } 
} 

// Usage: 
BigObject boo = new BigObject.Builder() 
    .setField1(/* whatever */) 
    .setField2(/* whatever */) 
    .setField3(/* whatever */) 
    .setField4(/* whatever */) 
    .setField5(/* whatever */) 
    .build(); 

Sie können Verifikationslogik auch in Builder-Set ..() und Build() -Methoden einfügen.

+0

Was würden Sie empfehlen, wenn viele Ihrer Felder "final" sind? Das ist die Hauptsache, die mich davon abhält, Hilfsfunktionen zu schreiben. Ich nehme an, ich kann die Felder privat machen und sicherstellen, dass ich sie im Klassencode nicht falsch modifiziere, aber ich hoffe auf etwas Eleganteres. – ragerdl

Verwandte Themen