Ist es eine gute oder schlechte Idee, Setter in Java dazu zu bringen, "dies" zurückzugeben?
public Employee setName(String name){
this.name = name;
return this;
}
Dieses Muster kann nützlich sein, da Sie dann Setter wie folgt verketten können:
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
an Stelle von:
Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);
... aber es widerspricht irgendwie der Standardkonvention. Ich nehme an, es könnte sich lohnen, nur weil es diesen Setter dazu bringen kann, etwas anderes Nützliches zu tun. Ich habe gesehen, dass dieses Muster an einigen Stellen verwendet wurde (z. B. JMock, JPA), aber es scheint ungewöhnlich und wird nur allgemein für sehr gut definierte APIs verwendet, bei denen dieses Muster überall verwendet wird.
Aktualisieren:
Was ich beschrieben habe, ist offensichtlich gültig, aber was ich wirklich suche, sind einige Gedanken darüber, ob dies allgemein akzeptabel ist und ob es Fallstricke oder verwandte Best Practices gibt. Ich kenne das Builder-Muster, aber es ist etwas komplizierter als das, was ich beschreibe - wie Josh Bloch es beschreibt, gibt es eine zugehörige statische Builder-Klasse für die Objekterstellung.
quelle
this
. Manchmal ändere ich die Funktion sogar so, dass sie, anstatt einen Wert zurückzugeben, auf ein Mitglied des Objekts angewendet wird, nur damit ich dies tun kann. Es ist wundervoll. :)Antworten:
Ich glaube nicht, dass irgendetwas speziell daran falsch ist, es ist nur eine Frage des Stils. Es ist nützlich, wenn:
Alternativen zu dieser Methode könnten sein:
Wenn Sie nur einige Eigenschaften gleichzeitig festlegen, würde ich sagen, dass es sich nicht lohnt, "dies" zurückzugeben. Es fällt auf jeden Fall herunter, wenn Sie sich später dazu entschließen, etwas anderes zurückzugeben, z. B. einen Status- / Erfolgsindikator / eine Nachricht.
quelle
Es ist keine schlechte Praxis. Es ist eine zunehmend übliche Praxis. In den meisten Sprachen müssen Sie sich nicht mit dem zurückgegebenen Objekt befassen, wenn Sie dies nicht möchten. Dadurch wird die Syntax für die "normale" Setter-Verwendung nicht geändert, sondern Sie können Setter miteinander verketten.
Dies wird üblicherweise als Builder-Muster oder fließende Schnittstelle bezeichnet .
Es ist auch in der Java-API üblich:
quelle
bla.foo.setBar1(...) ; bla.foo.setBar2(...)
beim Schreiben vermeidetbla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)
(Zeilenumbrüche können in einem SO-Kommentar wie diesem nicht verwendet werden :-( ... ich hoffe, Sie bekommen den Punkt, wenn Sie 10 solcher Setter oder komplexere Aufrufe in Betracht ziehen)Zusammenfassen:
Ein paar andere Punkte nicht erwähnt:
Dies verstößt gegen das Prinzip, dass jede Funktion eine (und nur eine) Sache tun sollte. Sie können oder können nicht daran glauben, aber in Java glaube ich, dass es gut funktioniert.
IDEs generieren diese nicht für Sie (standardmäßig).
Ich endlich, hier ist ein realer Datenpunkt. Ich hatte Probleme mit einer solchen Bibliothek. Der Abfrage-Generator von Hibernate ist ein Beispiel dafür in einer vorhandenen Bibliothek. Da die set * -Methoden von Query Abfragen zurückgeben, ist es unmöglich, anhand der Signatur zu erkennen, wie sie verwendet werden sollen. Beispielsweise:
Dies führt zu einer Mehrdeutigkeit: Ändert die Methode das aktuelle Objekt (Ihr Muster) oder ist Query möglicherweise wirklich unveränderlich (ein sehr beliebtes und wertvolles Muster), und die Methode gibt ein neues zurück. Dies erschwert lediglich die Verwendung der Bibliothek, und viele Programmierer nutzen diese Funktion nicht. Wenn Setter Setter wären, wäre es klarer, wie man sie benutzt.
quelle
this
ist kaum eine komplexe Raketenwissenschaft. :-)Ich bevorzuge die Verwendung von 'with'-Methoden dafür:
So:
Warnung : Diese
withX
Syntax wird häufig verwendet, um "Setter" für unveränderliche Objekte bereitzustellen. Daher können Aufrufer dieser Methoden vernünftigerweise erwarten, dass sie neue Objekte erstellen, anstatt die vorhandene Instanz zu mutieren. Vielleicht wäre eine vernünftigere Formulierung so etwas wie:Mit der Namenskonvention chainetXyz () sollte praktisch jeder zufrieden sein.
quelle
get
, einset
und ein habenwith
. Es ist jedoch immer noch eine interessante Lösung. :)Project Lombok
den@Getter/@Setter
Anmerkungen hinzufügen würden ... wäre das fantastisch für die Verkettung. Oder Sie könnten etwas wie denKestrel
Kombinator verwenden ( github.com/raganwald/Katy) ) JQuery- und Javascript-Freunde verwenden.with
Präfix ist eine andere Konvention. als @qualidafial gab ein Beispiel. Methoden mit dem Präfixwith
sollten nicht zurückgebenthis
, sondern eine neue Instanz wie die aktuelle Instanz, aberwith
diese Änderung. Dies geschieht, wenn Ihre Objekte unveränderlich sein sollen. Wenn ich also eine Methode sehe, der ein Präfix vorangestellt istwith
, gehe ich davon aus, dass ich ein neues Objekt erhalte, nicht dasselbe Objekt.Wenn Sie nicht
'this'
vom Setter zurückkehren möchten, aber nicht die zweite Option verwenden möchten, können Sie die folgende Syntax verwenden, um Eigenschaften festzulegen:Abgesehen davon denke ich, dass es in C # etwas sauberer ist:
quelle
equals
Methode verpfuscht hat . Es gibt sehr saubere Möglichkeiten, mit anonymen Klassen umzugehen,equals
wenn Sie wissen, was Sie tun.Es bricht nicht nur die Konvention von Gettern / Setzern, sondern auch das Referenzframework für Java 8-Methoden.
MyClass::setMyValue
ist einBiConsumer<MyClass,MyValue>
undmyInstance::setMyValue
ist einConsumer<MyValue>
. Wenn Ihr Setter zurückgegeben wirdthis
, handelt es sich nicht mehr um eine gültige Instanz vonConsumer<MyValue>
, sondern um eineFunction<MyValue,MyClass>
und führt dazu, dass alles, was Methodenreferenzen auf diese Setter verwendet (vorausgesetzt, es handelt sich um ungültige Methoden), unterbrochen wird.quelle
Consumer<A>
alsFunction<A,B>
auch eine Standardimplementierung von bereitstelltvoid accept(A a) { apply(a); }
. Dann kann es leicht als eines von beiden verwendet werden und bricht keinen Code, der ein bestimmtes Formular erfordert.Ich kenne Java nicht, aber ich habe dies in C ++ getan. Andere Leute haben gesagt, es macht die Zeilen sehr lang und sehr schwer zu lesen, aber ich habe es oft so gemacht:
Das ist noch besser:
Zumindest denke ich. Aber Sie können mich gerne herabstimmen und mich einen schrecklichen Programmierer nennen, wenn Sie es wünschen. Und ich weiß nicht, ob Sie dies überhaupt in Java tun dürfen.
quelle
//
nach jeder Methode, wenn Sie Ihre IDE so konfigurieren, dass bereits umbrochene Zeilen nicht verknüpft werden (z. B. Eclipse> Eigenschaften> Java> Codestil> Formatierer> Zeilenumbruch> Niemals bereits umbrochene Zeilen verknüpfen).Dieses Schema (Wortspiel beabsichtigt), das als "fließende Schnittstelle" bezeichnet wird, wird jetzt ziemlich populär. Es ist akzeptabel, aber es ist nicht wirklich meine Tasse Tee.
quelle
Da es nicht void zurückgibt, ist es kein gültiger JavaBean-Eigenschaftssetter mehr. Dies kann von Bedeutung sein, wenn Sie einer der sieben Menschen auf der Welt sind, die visuelle "Bean Builder" -Tools verwenden, oder einer der 17, die JSP-bean-setProperty-Elemente verwenden.
quelle
Zumindest theoretisch kann dies die Optimierungsmechanismen der JVM beschädigen, indem falsche Abhängigkeiten zwischen Aufrufen festgelegt werden.
Es soll syntaktischer Zucker sein, kann aber tatsächlich Nebenwirkungen in der virtuellen Maschine des superintelligenten Java 43 hervorrufen.
Deshalb stimme ich nein, benutze es nicht.
quelle
set
Methode ausgeführt werden soll, hängt von der erstenset
Methode ab, obwohl es dem Programmierer bekannt ist.-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining
Das java7 jdk integriert definitiv verkettete Methoden und führt ungefähr die gleiche Anzahl von Iterationen durch, die erforderlich sind, um Leersetzer als heiß zu markieren und sie auch zu inline. Denken Sie, Sie unterschätzen die Leistungsfähigkeit der Opcode-Bereinigungsalgorithmen der JVM. Wenn es weiß, dass Sie dies zurückgeben, überspringt es den Opcode jrs (Java Return Statement) und belässt dies einfach auf dem Stapel.Es ist überhaupt keine schlechte Praxis. Es ist jedoch nicht mit JavaBeans Spec kompatibel .
Und es gibt viele Spezifikationen, die von diesen Standard-Accessoren abhängen.
Sie können sie jederzeit nebeneinander existieren lassen.
Jetzt können wir sie zusammen verwenden.
Hier kommt eine andere Version für unveränderliche Objekte.
Jetzt können wir das tun.
quelle
some
Feld fest. Zweitens sollten Sie einen Schutz hinzufügen und in build () festlegensome
,null
damit erSome
wirklich unveränderlich ist. Andernfalls können Siebuilder.value()
dieselbe Builder-Instanz erneut aufrufen . Und schließlich haben Sie ja einen Builder, aber Sie habenSome
immer noch einen öffentlichen Konstruktor, was bedeutet, dass Sie die Verwendung des Builders nicht offen befürworten, dh der Benutzer weiß nichts anderes davon, als eine Methode zum Festlegen einer benutzerdefinierten Methode auszuprobieren oder nach ihr zu suchenvalue
überhaupt.Wenn Sie dieselbe Konvention in der gesamten Anwendung verwenden, scheint dies in Ordnung zu sein.
Wenn andererseits ein vorhandener Teil Ihrer Anwendung die Standardkonvention verwendet, würde ich mich daran halten und Builder zu komplizierteren Klassen hinzufügen
quelle
Paulo Abrantes bietet eine andere Möglichkeit, JavaBean-Setter fließend zu machen: Definieren Sie für jede JavaBean eine innere Builder-Klasse. Wenn Sie Tools verwenden, die von Setzern, die Werte zurückgeben, durcheinander gebracht werden, kann das Muster von Paulo hilfreich sein.
quelle
Ich bin für Setter, die "dies" zurückgeben. Es ist mir egal, ob es nicht Bohnen-konform ist. Wenn es in Ordnung ist, den Ausdruck / die Anweisung "=" zu haben, sind Setter, die Werte zurückgeben, in Ordnung.
quelle
Früher habe ich diesen Ansatz bevorzugt, aber ich habe mich dagegen entschieden.
Gründe dafür:
Das Builder-Muster, das ich gesehen habe, verwendet nicht die setFoo (foo) .setBar (bar) -Konvention, sondern mehr foo (foo) .bar (bar). Vielleicht aus genau diesen Gründen.
Es ist wie immer Geschmackssache. Ich mag nur den Ansatz der "geringsten Überraschungen".
quelle
Dieses spezielle Muster wird als Methodenverkettung bezeichnet. Wikipedia-Link , hier finden Sie weitere Erklärungen und Beispiele dafür, wie es in verschiedenen Programmiersprachen gemacht wird.
PS: Ich habe gerade daran gedacht, es hier zu lassen, da ich nach dem spezifischen Namen gesucht habe.
quelle
Auf den ersten Blick: "Grässlich!".
Bei weiterem Nachdenken
ist eigentlich weniger fehleranfällig als
Also ziemlich interessant. Idee zur Werkzeugtasche hinzufügen ...
quelle
list
am Anfang keine Redundanz gibt .Ja, ich denke es ist eine gute Idee.
Wenn ich etwas hinzufügen könnte, was ist mit diesem Problem:
Das wird funktionieren :
Dies wird von Eclipse nicht akzeptiert! ::
Dies liegt daran, dass setName () ein Volk und keinen Freund zurückgibt und es keinen PeoplesetNickName gibt.
Wie können wir Setter schreiben, um die SELF-Klasse anstelle des Klassennamens zurückzugeben?
So etwas wäre in Ordnung (wenn das Schlüsselwort SELF existieren würde). Existiert das überhaupt?
quelle
class C<S extends C<S>>
, was sicherer ist als eine EbeneS extends C
. a) WennA extends B
, undB extends C<B>
und Sie ein A haben, wissen Sie nur, dass a B wird zurückgegeben, es sei denn, A überschreibt jede einzelne Methode. b) Sie können kein lokales, nicht rohes bezeichnenC<C<C<C<C<...>>>>>
.Im Allgemeinen ist dies eine gute Vorgehensweise, aber Sie müssen möglicherweise für Funktionen vom Typ set den Booleschen Typ verwenden, um festzustellen, ob der Vorgang erfolgreich abgeschlossen wurde oder nicht. Dies ist auch eine Möglichkeit. Im Allgemeinen gibt es kein Dogma zu sagen, dass dies gut oder Bett ist, es kommt natürlich von der Situation.
quelle
Aus der Aussage
Ich sehe zwei Dinge
1) Bedeutungslose Aussage. 2) Unleserlichkeit.
quelle
Dies ist möglicherweise weniger lesbar
oder dieses
Dies ist viel besser lesbar als:
quelle
Ich mache meine Setter schon eine ganze Weile und das einzige wirkliche Problem sind Bibliotheken, die sich an die strengen getPropertyDescriptors halten, um die Bean-Reaktoren für Bean-Reader / Writer zu erhalten. In diesen Fällen verfügt Ihre Java-Bean nicht über die erwarteten Writer.
Zum Beispiel habe ich es nicht sicher getestet, aber ich wäre nicht überrascht, dass Jackson diese beim Erstellen von Java-Objekten aus json / maps nicht als Setter erkennt. Ich hoffe, ich irre mich in diesem Punkt (ich werde es bald testen).
Tatsächlich entwickle ich ein leichtes SQL-zentriertes ORM und muss erkannten Setzern, die dies zurückgeben, Code über getPropertyDescriptors hinzufügen.
quelle
Vor langer Zeit antworten, aber meine zwei Cent ... Es ist in Ordnung. Ich wünschte, diese fließende Schnittstelle würde öfter verwendet.
Durch Wiederholen der Variablen 'factory' werden unten keine weiteren Informationen hinzugefügt:
Das ist sauberer, imho:
Natürlich müsste, wie eine der bereits erwähnten Antworten, die Java-API optimiert werden, um dies in bestimmten Situationen wie Vererbung und Tools korrekt durchzuführen.
quelle
Es ist besser, andere Sprachkonstrukte zu verwenden, falls verfügbar. In Kotlin würden Sie beispielsweise mit verwenden , anwenden oder lassen . Wenn Sie diesen Ansatz, werden Sie nicht wirklich brauchen eine Instanz von Ihrem Setter zurückzukehren.
Dieser Ansatz ermöglicht es Ihrem Client-Code:
Hier sind einige Beispiele.
quelle
Wenn ich eine API schreibe, verwende ich "return this", um Werte festzulegen, die nur einmal festgelegt werden. Wenn ich andere Werte habe, die der Benutzer ändern kann, verwende ich stattdessen einen Standard-Leersetzer.
Es ist jedoch wirklich eine Frage der Präferenz und Verkettungssetter sehen meiner Meinung nach ziemlich cool aus.
quelle
Ich bin mit allen Postern einverstanden, die behaupten, dies verstoße gegen die JavaBeans-Spezifikation. Es gibt Gründe, dies beizubehalten, aber ich bin auch der Meinung, dass die Verwendung dieses Builder-Musters (auf das angespielt wurde) seinen Platz hat. Solange es nicht überall verwendet wird, sollte es akzeptabel sein. "It's Place" ist für mich der Endpunkt eines Aufrufs einer "build ()" - Methode.
Es gibt natürlich auch andere Möglichkeiten, all diese Dinge festzulegen, aber der Vorteil hierbei ist, dass 1) öffentliche Konstruktoren mit vielen Parametern und 2) teilweise spezifizierte Objekte vermieden werden. Hier muss der Builder erfassen, was benötigt wird, und am Ende "build ()" aufrufen, um sicherzustellen, dass kein teilweise angegebenes Objekt erstellt wird, da dieser Vorgang weniger als öffentlich sichtbar sein kann. Die Alternative wären "Parameterobjekte", aber das IMHO schiebt das Problem nur um eine Ebene zurück.
Ich mag Konstruktoren mit vielen Parametern nicht, weil sie es wahrscheinlicher machen, dass viele Argumente des gleichen Typs übergeben werden, was es einfacher machen kann, die falschen Argumente an Parameter zu übergeben. Ich mag es nicht, viele Setter zu verwenden, da das Objekt verwendet werden könnte, bevor es vollständig konfiguriert ist. Darüber hinaus wird der Gedanke, Standardwerte basierend auf vorherigen Auswahlmöglichkeiten zu haben, mit einer "build ()" - Methode besser bedient.
Kurz gesagt, ich denke, es ist eine gute Praxis, wenn es richtig angewendet wird.
quelle
Schlechte Angewohnheit: Ein Setter hat einen Getter bekommen
Was ist mit der expliziten Deklaration einer Methode, die dies für U tut?
quelle