Konstruktoren mit vielen Parametern in Java verwalten

105

In einigen unserer Projekte gibt es eine Klassenhierarchie, die im Laufe der Kette weitere Parameter hinzufügt. Im unteren Bereich können einige Klassen bis zu 30 Parameter haben, von denen 28 nur an den Superkonstruktor übergeben werden.

Ich werde anerkennen, dass die Verwendung von automatisiertem DI über etwas wie Guice nett wäre, aber aus technischen Gründen sind diese spezifischen Projekte auf Java beschränkt.

Eine Konvention zum alphabetischen Ordnen der Argumente nach Typ funktioniert nicht, da ein Typ, der überarbeitet wurde (der Kreis, den Sie für Argument 2 übergeben haben, ist jetzt eine Form), plötzlich außer Betrieb sein kann.

Diese Frage ist möglicherweise zu spezifisch und voller Kritikpunkte: "Wenn das Ihr Problem ist, machen Sie es auf Designebene falsch", aber ich suche nur nach Gesichtspunkten.

Steve Armstrong
quelle

Antworten:

264

Das Builder-Entwurfsmuster kann hilfreich sein. Betrachten Sie das folgende Beispiel

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

Dadurch können wir Code wie schreiben

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

Wenn wir ein erforderliches Feld weglassen (vermutlich ist ein Name erforderlich), kann der Student-Konstruktor eine Ausnahme auslösen. Außerdem können wir Standard- / optionale Argumente verwenden, ohne dass wir irgendeine Art von Argumentreihenfolge verfolgen müssen, da jede Reihenfolge dieser Aufrufe gleich gut funktioniert.

Eli Courtwright
quelle
10
Natürlich müssen Sie bei statischen Importen diese "Builder" überhaupt nicht "sehen". Beispielsweise könnten Sie einen statischen Methodennamen (String name) haben, der einen Builder zurückgibt, und Student (StudentBuilder), der einen Student zurückgibt. Daher Student (Name ("Joe"). Alter (15) .Motto ("Ich habe mich nass gemacht"));
oxbow_lakes
2
@oxbow_lakes: Welche Klasse hat in Ihrem Beispiel einen statischen Methodennamen (String name)?
user443854
Technisch gesehen ist es möglich, die Schülerklasse zu verwenden, um einen neuen Schüler zu erstellen. Ich habe die Methoden in der Student-Klasse hinzugefügt und es hat gut funktioniert. Auf diese Weise brauchte ich keine weitere Builder-Klasse. Ich bin mir nicht sicher, ob dies wünschenswert ist. Gibt es einen Grund, eine andere Klasse (StudentBuilder) zum Erstellen zu verwenden?
WVrock
1
@WVrock: Das hängt von Ihrer Implementierung ab. Wie ich in meiner Antwort sage, kann dies möglicherweise dazu führen, dass die Klasse in einem halb initialisierten Zustand verbleibt, z. B. wenn Sie ein Pflichtfeld haben, das noch nicht initialisiert wurde.
Eli Courtwright
@EliCourtwright Ich denke, es geht um Präferenz / Code-Design. Anstatt den Konstruktor die Ausnahme auslösen zu lassen, habe ich die buildStudent()Methode die Ausnahme auslösen lassen.
WVrock
24

Können Sie verwandte Parameter in ein Objekt einkapseln?

zB wenn Parameter wie sind


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

dann könnten Sie stattdessen haben:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}

JeeBee
quelle
8

Nun, die Verwendung des Builder-Musters könnte eines sein Lösung sein.

Aber wenn Sie zu 20 bis 30 Parametern kommen, würde ich vermuten, dass es eine hohe Beziehung zwischen den Parametern gibt. Daher ist es (wie vorgeschlagen) wahrscheinlich am sinnvollsten, sie in logisch vernünftige Datenobjekte zu verpacken. Auf diese Weise kann das Datenobjekt bereits die Gültigkeit von Einschränkungen zwischen den Parametern überprüfen.

Bei all meinen Projekten in der Vergangenheit konnte ich den Code bereinigen, indem ich ein besseres Datenmodell erstellte, als ich zu viele Parameter hatte (und das waren 8, nicht 28!).


quelle
4

Da Sie auf Java 1.4 beschränkt sind, wäre Spring eine sehr anständige Option , wenn Sie DI möchten . DI ist nur an Stellen hilfreich, an denen die Konstruktorparameter Dienste sind oder sich zur Laufzeit nicht ändern.

Wenn Sie all diese verschiedenen Konstruktoren haben, weil Sie variable Optionen zum Erstellen eines Objekts wünschen, sollten Sie ernsthaft die Verwendung des Builder-Musters in Betracht ziehen.

Guðmundur Bjarni
quelle
Die Parameter sind meistens Dienste, wie Sie erwähnt haben, und daher ist DI das, was ich brauchen würde. Ich denke, das in einigen anderen Antworten erwähnte Builder-Muster ist genau das, was ich mir erhofft hatte.
Steve Armstrong
4

Die beste Lösung besteht darin, nicht zu viele Parameter im Konstruktor zu haben. Nur Parameter, die im Konstruktor wirklich benötigt werden, sind Parameter, die zum korrekten Initialisieren des Objekts erforderlich sind. Sie können Konstruktoren mit mehreren Parametern haben, aber auch einen Konstruktor mit nur den minimalen Parametern. Die zusätzlichen Konstruktoren rufen diesen einfachen Konstruktor auf und setzen danach die anderen Parameter. Auf diese Weise können Sie das Kettenproblem mit immer mehr Parametern vermeiden, haben aber auch einige Convenience-Konstruktoren.

Mnementh
quelle
2

Ich kann die Verwendung von Immutables oder POJOBuilder bei Verwendung des Builder-Musters wirklich empfehlen .

Tomas Bjerre
quelle
1

Refactoring, um die Anzahl der Parameter und die Tiefe Ihrer Vererbungshierarchie zu reduzieren, ist so ziemlich alles, was ich mir vorstellen kann, denn nichts wird wirklich dazu beitragen, 20 Parameter gerade zu halten. Sie müssen nur jeden einzelnen Anruf tätigen, während Sie sich die Dokumentation ansehen.

Eine Sache, die Sie tun könnten, ist, einige logisch gruppierte Parameter in einem eigenen übergeordneten Objekt zu gruppieren, aber das hat seine eigenen Probleme.

Aaron Maenpaa
quelle