2009-05-06 4 views
24

Im Folgenden sind die zwei Ansätze:Konstruktor mit allen Klasseneigenschaften oder Standardkonstruktor mit Setter?

  • Konstruktor mit allen Klasseneigenschaften

Pros: Ich habe eine genaue Anzahl von Typen von Parametern eingeben, damit, wenn ich einen Fehler mache die Compiler mich warnt (Übrigens, gibt es eine Möglichkeit, das Problem zu verhindern, dass zwei Integer fälschlicherweise auf die Parameterliste geschaltet wurden?)

Nachteile: Wenn ich viele Eigenschaften habe, kann die Instantiierungslinie sehr lang werden und sie könnte sich über zwei oder mehr erstrecken mehr Linien

  • Setter und der Standard leere Konstruktor

Pros: Ich kann klar sehen, was ich einstellen, so dass, wenn ich etwas falsch tue ich es so schnell lokalisieren kann, wie ich es bin eingeben (Ich kann den vorherigen Fehler nicht machen, zwei Variablen desselben Typs zu wechseln)

Nachteile: die Instanziierung eines Objekts mit vielen Eigenschaften könnte mehrere Zeilen dauern (weiß nicht, ob dies wirklich eine Con ist) und wenn Ich vergesse, eine Eigenschaft zu setzen, der Compiler sagt nichts.

Was werden Sie tun und warum? Kennen Sie ein beliebiges Lichtmuster (beachten Sie, dass es jedes Mal verwendet werden sollte, wenn ein Objekt mit 7 + Eigenschaften instanziiert wird), um es zu empfehlen? Ich frage das, weil ich große Konstruktoren nicht mag, wo ich nicht schnell herausfinden kann, wo die Variable ist, nach der ich suche, andererseits finde ich die "set all properties" anfällig dafür, einige der Eigenschaften zu verpassen .

meine Annahmen Sek Argument in Vor-und Nachteile, da sie nur mein Gedanken sind :)

Update - eine Frage, die ich gefunden habe, die im Zusammenhang mit dieser ist: Building big, immutable objects without using constructors having long parameter lists

Antwort

20

Sie könnten sich das Builder-Muster ansehen, das von Joshua Bloch empfohlen und in Effective Java beschrieben wird. Es gibt eine Präsentation mit den wichtigsten Punkten unter http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-2689.pdf; Zweifellos könntest du eine bessere Referenz ausgraben.

Grundsätzlich haben Sie eine andere Klasse, wahrscheinlich eine innere Klasse, die Methoden bereitstellt, die nach den festgelegten Eigenschaften benannt sind und die den ursprünglichen Builder zurückgeben, sodass Sie Aufrufe verketten können. Es macht einen ziemlich lesbaren Stück Code.

Nehmen wir zum Beispiel an, ich habe eine einfache Message mit ein paar Eigenschaften. Der Client-Code dieser Konstruktion könnte einen Builder verwenden, um eine Message wie folgt herzustellen:

Message message = new Message.Builder() 
    .sender(new User(...)) 
    .recipient(new User(...)) 
    .subject("Hello, world!") 
    .text(messageText) 
    .build(); 

Ein Fragment von Message.Builder könnte ähnlich der folgenden aussehen:

public class Builder { 

    private User sender = null; 
    // Other properties 

    public Builder sender(User sender) { 
     this.sender = sender; 
     return this; 
    } 
    // Methods for other properties 

    public Message build() { 
     Message message = new Message(); 
     message.setSender(sender); 
     // Set the other properties 
     return message; 
    } 

} 
+0

Danke für den erklärenden Code –

+0

Builder sind wirklich nett, wenn Sie ein komplexes Objekt haben. Ich kann es sehr empfehlen. –

+0

und das Buch! Es ist wirklich ein Muss für Java-Programmierer lesen - es ist verfügbar auf O'Reilly Safari, wo ich es gelesen habe (wir haben das kleinste Abonnement) –

26

Sie verpasst haben die Der größte Vorteil eines Konstruktors mit vielen Parametern: Er ermöglicht es Ihnen, unveränderliche Typen zu erstellen.

Der normale Weg unveränderlichen Typen ohne riesige Konstruktor Bösartigkeit zu schaffen ist ein Helfer-Typ zu haben - ein Builder, die die Werte hält Sie in Ihrem letzten Objekt wollen werden, dann baut das unveränderliche Objekt wenn Sie bereit.

+0

Ist ein Builder nicht die bessere Strategie überhaupt? – Uri

+0

Wenn der Status Ihres Objekts nicht ausgeführt werden kann, ohne dass die Eigenschaften festgelegt wurden, erzwingen Sie den Benutzer, indem Sie den Konstruktor durchlaufen. –

+0

@Uri: Das Builder-Muster ist über der Spitze IMO, wenn Sie nur ein paar Eigenschaften haben. –

6

Jüngste wissenschaftliche Untersuchungen (CMU und Microsoft) zur API-Usability legen nahe, dass Standardkonstruktoren mit Settors im Hinblick auf die Benutzerfreundlichkeit der beste Weg wären. Dies ist von „Usability Auswirkungen der Parameter in Objekten Konstrukteurs-Erfordern“ von Jeff Stylos und Steven Clarke und wurde bei der Internationalen Konferenz über Software Engineering präsentiert:

Abstract: Die Verwendbarkeit von APIs immer wichtiger ist, die Produktivität Programmierern. Basierend auf Erfahrungen mit Usability-Studien spezifischer APIs wurden Techniken zur Untersuchung der Verwendbarkeit von Design-Optionen untersucht, die vielen APIs gemeinsam sind. Eine Vergleichsstudie wurde durchgeführt, um zu bewerten, wie professionelle Programmierer APIs mit erforderlichen Parametern in Konstruktoren von Objekten verwenden, im Gegensatz zu parameterlosen "Standard" -Konstruktoren. Es wurde die Hypothese aufgestellt, dass die erforderlichen Parameter mehr brauchbare und selbstdokumentierende APIs erzeugen würden, indem Programmierer zur korrekten Verwendung von Objekten geleitet werden und Fehler vermieden werden.In der Studie wurde jedoch festgestellt, dass Programmierer wider Erwarten stark bevorzugten und effektiver mit APIs waren, die keine Konstruktorparameter benötigten. Das Verhalten der Teilnehmer wurde unter Verwendung des kognitiven Dimensions-Frameworks analysiert, und es wurde aufgezeigt, dass die erforderlichen Konstruktor-Parameter mit den üblichen Lernstrategien interferieren und eine unerwünschte vorzeitige Bindung verursachen.

0

Es gibt auch noch andere Aspekte. Wenn Sie bestimmte Dinge zur Entwurfszeit und nicht nur zur Laufzeit mit Ihrer Klasse ausführen möchten, z. B. indem Sie Ihre Klasse als Objekt in die Objektpalette einfügen (dies ist Java mit Netbeans), müssen Sie einen Konstruktor ohne Argumenten angeben um dies zu können.

+0

Dies ist eine wichtige Überlegung - ORM-Tools (z. B. Hibernate, obwohl ich mir nicht sicher bin, ob die neueste Version dies noch tut) benötigen barrierefreie Setter und einen Standardkonstruktor. – hromanko

3

Sie erwähnen es in Ihrem Beitrag, aber ich denke, dies ist ein wichtiger Punkt, der mehr Aufmerksamkeit verdient: Wenn nicht jeder Eingabeparameter ein anderer Typ ist, ist das große Problem mit riesigen Konstruktoren, dass es sehr ist, ein paar zu transponieren von Variablen. Der Compiler ist ein unzuverlässiges Sicherheitsnetz - er wird einige Fehler machen, aber diejenigen, die durchkommen, werden viel schwieriger zu identifizieren und zu debuggen sein. Vor allem, weil die Eingabeliste für einen riesigen Konstruktor ziemlich undurchsichtig ist, es sei denn, Sie haben die API in einem anderen Fenster geöffnet.

Getter und Setter sind wesentlich einfacher zu debuggen, insbesondere wenn Sie Sicherheitsmaßnahmen einleiten, die eine Laufzeitausnahme auslösen, wenn das Objekt nicht ordnungsgemäß befüllt ist. Und ich bin ein großer Fan von "einfach zu debuggen."

Vor diesem Thread hatte ich noch nie von dem Builder-Muster gehört, das Rob erwähnt. Habe es nie selbst benutzt (offensichtlich), aber es ist verdammt faszinierend.

+0

Wie werden Sicherheitsvorkehrungen getroffen, die eine Laufzeitausnahme auslösen, wenn das Objekt nicht ordnungsgemäß befüllt ist? – mayu

0

Auch hier gibt es andere Strategien. Bevor ich versuche, herauszufinden, wie ich mit vielen Parametern umgehen soll, ist es wichtig, dass Sie Ihr Design erneut besuchen und prüfen, ob Ihre Klasse zu viel tut. Sehen Sie, ob Sie einige der Parameter in einer neuen Klasse zusammenfassen und etwas Verhalten in diese Klasse verschieben können.

2

Ich bevorzuge Konstruktor Argumente, aus den oben genannten Gründen der Unveränderlichkeit.Wenn Ihnen das einen Konstruktor gibt, der viele Argumente benötigt (sagen wir mehr als vier), ist das ein Code-Geruch für mich: einige dieser Argumente sollten zu ihren eigenen Typen zusammengefasst werden.

Zum Beispiel, wenn Sie so etwas wie diese:

class Contact 
{ 
    public Contact(string firstName, string lastName, string phoneNumber, 
     string street, string city, string state, int zipCode) { ... } 
} 

Ich würde Refactoring es an:

class Contact 
{ 
    public Contact(Person person, PhoneNumber number, Address address) { ... } 
} 

class Person 
{ 
    public Person(string firstName, string lastName) { ... } 
} 

class PhoneNumber 
{ 
    public PhoneNumber(string digits) { ... } 
} 

class Address 
{ 
    public Address(string street, string city, string state, int zipCode) { ... } 
} 

zu großen Klassen sind ein wirklich gemeinsames Designproblem in OOP Codebases.

+0

Wie gehen Sie mit zipCode um? –

+0

Konstruktor der Überladungsadresse. – munificent

0

Wer sagt, dass Sie beides nicht tun können? Ich würde sagen, dass obligatorische Eigenschaften in den Konstruktor gehen, optionale werden mit Setter behandelt. BTW, wer sagt, dass Sie immer einen Setter pro Eigentum benötigen? Wenn zwei Eigenschaften konzeptuell zusammengehören, warum sollten sie nicht zusammen gesetzt werden?

Ich mag auch das Builder-Muster, aber die wichtigste Regel ist: Benutze immer dein Gehirn und finde das Design, das am besten zu dem spezifischen Problem passt. Es gibt keine universelle Lösung.

Verwandte Themen