it-swarm.com.de

Ist es schlechte Praxis, einen Setzer dazu zu bringen, "dies" zurückzugeben?

Ist es eine gute oder schlechte Idee, dass Setter in Java "dieses" zurückgeben?

public Employee setName(String name){
   this.name = name;
   return this;
}

Dieses Muster kann nützlich sein, da Sie 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 der üblichen Konvention. Ich nehme an, es könnte sich lohnen, nur weil es den Setzer dazu bringen kann, etwas anderes zu tun. Ich habe gesehen, dass dieses Muster an einigen Stellen verwendet wurde (z. B. JMock, JPA), aber es scheint ungewöhnlich und wird im Allgemeinen nur 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 generell akzeptabel ist und ob es Fallstricke oder verwandte Best Practices gibt. Ich weiß über das Builder-Muster Bescheid, aber es ist etwas komplizierter als das, was ich beschreibe - wie es von Josh Bloch beschrieben wird, gibt es eine zugehörige statische Builder-Klasse für die Objekterstellung.

220
Ken Liu

Ich glaube nicht, dass irgendetwas speziell falsch ist, es ist nur eine Frage des Stils. Es ist nützlich, wenn:

  • Sie müssen viele Felder gleichzeitig festlegen (einschließlich beim Erstellen).
  • sie wissen, welche Felder Sie einstellen müssen, wenn Sie den Code schreiben, und 
  • es gibt viele verschiedene Kombinationen, für die Sie Felder festlegen möchten.

Alternativen zu dieser Methode könnten sein:

  1. Ein Mega-Konstruktor (Nachteil: Sie können viele Nullen oder Standardwerte übergeben, und es wird schwer zu wissen, welcher Wert welchem ​​entspricht).
  2. Mehrere überladene Konstruktoren (Nachteil: wird schwer, wenn Sie mehr als ein paar haben)
  3. Fabrik-/statische Methoden (Nachteil: wie überladene Konstruktoren - wird unhandlich, wenn mehr als ein paar vorhanden sind)

Wenn Sie nur ein paar Eigenschaften auf einmal einstellen, würde ich sagen, dass es sich nicht lohnt, 'this' zurückzugeben. Wenn Sie später etwas anderes zurücksenden möchten, z. B. Status-/Erfolgskennzeichen/-meldung, fällt dies auf jeden Fall auf.

68
Tom Clift

Es ist keine schlechte Praxis. Es ist eine zunehmend verbreitete Praxis. In den meisten Sprachen müssen Sie sich nicht mit dem zurückgegebenen Objekt befassen, wenn Sie dies nicht möchten. Es ändert also nicht die "normale" Setter-Verwendungssyntax, sondern ermöglicht die Verkettung von Setters.

Dies wird im Allgemeinen als Builder-Pattern oder als fließende Schnittstelle bezeichnet.

Es ist auch üblich in der Java-API:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
95
cletus

Zusammenfassen:

  • es wird als "fließende Schnittstelle" oder "Method Chaining" bezeichnet.
  • dies ist kein "Standard" Java, obwohl Sie es heutzutage mehr und mehr sehen (funktioniert prima in jQuery)
  • es verstößt gegen die JavaBean-Spezifikation und wird daher mit verschiedenen Tools und Bibliotheken, insbesondere mit JSP-Buildern und Spring, brechen.
  • dies kann einige Optimierungen verhindern, die die JVM normalerweise ausführen würde
  • einige Leute denken, es reinigt den Code, andere denken, es sei "gruselig"

Einige andere Punkte, die nicht erwähnt wurden:

  • Dies verstößt gegen das Prinzip, dass jede Funktion eine (und nur eine) Sache ausführen sollte. Sie mögen vielleicht nicht daran glauben, aber in Java glaube ich, dass es gut funktioniert.

  • IDEs werden diese (standardmäßig) nicht für Sie generieren.

  • Ich bin endlich ein realer Datenpunkt. Ich habe Probleme bei der Verwendung einer so erstellten Bibliothek. Der Abfrage-Generator von Hibernate ist ein Beispiel dafür in einer vorhandenen Bibliothek. Da die set * -Methoden von Query Abfragen zurückgeben, kann man nicht einfach anhand der Signatur erkennen, wie sie verwendet wird. Zum Beispiel:

    Query setWhatever(String what);
    
  • Es führt zu einer Zweideutigkeit: Ändert die Methode das aktuelle Objekt (Ihr Muster) oder ist Query möglicherweise unveränderlich (ein sehr beliebtes und wertvolles Muster), und die Methode gibt ein neues zurück. Dadurch wird die Verwendung der Bibliothek schwieriger, und viele Programmierer nutzen diese Funktion nicht. Wenn Setter Setter wären, wäre es klarer, wie man es benutzt.

76
ndp

Ich bevorzuge die Verwendung von Mit-Methoden:

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

Somit:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));
75
qualidafial

Wenn Sie 'this' nicht vom Setter zurückgeben möchten, die zweite Option jedoch nicht verwenden möchten, können Sie die folgende Syntax zum Festlegen von Eigenschaften verwenden:

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

Nebenbei denke ich, dass es in C # etwas sauberer ist:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
24
Luke Quinane

Es bricht nicht nur die Konvention der Getter/Setter, sondern auch das Referenzframework der Java 8-Methode. MyClass::setMyValue ist ein BiConsumer<MyClass,MyValue> und myInstance::setMyValue ist ein Consumer<MyValue>. Wenn Ihr Setter this zurückgegeben hat, ist dies keine gültige Instanz von Consumer<MyValue> mehr, sondern ein Function<MyValue,MyClass>. Dies führt dazu, dass alles, was Methodenreferenzen auf diese Setter verwendet (vorausgesetzt, es handelt sich um ungültige Methoden), um zu brechen.

9
Steve K

Ich weiß nicht, Java, aber ich habe das in C++ gemacht.

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

Das ist noch besser:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

zumindest denke ich. Aber Sie können mich gerne abstimmen und einen schrecklichen Programmierer nennen, wenn Sie möchten. Und ich weiß nicht, ob Sie das überhaupt in Java machen dürfen.

7
Carson Myers

Da es nicht void zurückgibt, ist es kein gültiger JavaBean-Eigenschaftssetzer mehr. Das könnte von Bedeutung sein, wenn Sie einer der sieben Menschen auf der Welt sind, der visuelle "Bean Builder" -Tools verwendet, oder einer der 17, die JSP-Bean-setProperty-Elemente verwenden.

6
Ken

Zumindestkann theoretischdie Optimierungsmechanismen der JVM beschädigt werden, indem falsche Abhängigkeiten zwischen Aufrufen festgelegt werden.

Es wird angenommen, dass es sich um syntaktischen Zucker handelt, aber in der virtuellen Maschine des hochintelligen Java 43 kann es tatsächlich zu Nebenwirkungen kommen.

Deshalb stimme ich nein, benutze es nicht.

5
Marian

Es ist überhaupt keine schlechte Praxis. Es ist jedoch nicht mit JavaBeans Spec kompatibel.

Und es gibt eine Menge Spezifikationen, die von diesen Standard-Accessoren abhängen.

Sie können sie immer dazu bringen, nebeneinander zu existieren.

public class Some {
    public String getValue() { // JavaBeans
        return value;
    }
    public void setValue(final String value) { // JavaBeans
        this.value = value;
    }
    public String value() { // simple
        return getValue();
    }
    public Some value(final String value) { // fluent/chaining
        setValue(value);
        return this;
    }
    private String value;
}

Jetzt können wir sie gemeinsam nutzen.

new Some().value("some").getValue();

Hier kommt eine andere Version für unveränderliches Objekt.

public class Some {

    public static class Builder {

        public Some build() { return new Some(value); }

        public Builder value(final String value) {
            this.value = value;
            return this;
        }

        private String value;
    }

    private Some(final String value) {
        super();
        this.value = value;
    }

    public String getValue() { return value; }

    public String value() { return getValue();}

    private final String value;
}

Jetzt können wir das schaffen.

new Some.Builder().value("value").build().getValue();
5
Jin Kwon

Dieses Schema (Wortspiel beabsichtigt), das als "fließende Schnittstelle" bezeichnet wird, wird jetzt ziemlich populär. Es ist akzeptabel, aber es ist nicht wirklich mein Ding.

5
Noon Silk

Ich bin dafür, dass Setter "dieses" zurückbekommen. Es ist mir egal, ob es nicht Bohnen-konform ist. Wenn es in Ordnung ist, den Ausdruck "="/die Anweisung zu haben, dann sind Setter, die Werte zurückgeben, in Ordnung.

3
Monty Hall

Paulo Abrantes bietet eine weitere Möglichkeit, JavaBean-Setter fließend zu machen: Definieren Sie eine innere Builder-Klasse für jede JavaBean. Wenn Sie Werkzeuge verwenden, die von Setters, die Werte zurückgeben, überlistet werden, könnte das Muster von Paulo helfen.

3
Jim Ferrans

Wenn Sie dieselbe Konvention in der gesamten Anwendung verwenden, scheint es in Ordnung zu sein.

Wenn ein vorhandener Teil Ihrer Anwendung Standardkonventionen verwendet, würde ich mich dagegen halten und komplizierteren Klassen Builder hinzufügen

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}
2
Marcin Szymczak

Dieses bestimmte Muster wird als Method Chaining bezeichnet. Wikipedia-Link , dies hat mehr Erklärungen und Beispiele dafür, wie es in verschiedenen Programmiersprachen gemacht wird.

P.S: Ich dachte nur daran, es hier zu lassen, da ich nach dem Namen gesucht habe.

2
capt.swag

Früher habe ich diesen Ansatz vorgezogen, aber ich habe mich dagegen entschieden.

Gründe dafür:

  • Lesbarkeit. Dadurch wird der Code besser lesbar, wenn sich setFoo () in einer separaten Zeile befindet. Sie lesen den Code normalerweise viel, viel mehr als beim einmaligen Schreiben.
  • Nebeneffekt: setFoo () sollte nur Field Foo setzen, sonst nichts. Dies zurückzugeben ist ein zusätzliches "WAS war das".

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 die "am wenigsten überraschenden" Ansätze.

Auf den ersten Blick: "Grässlich!".

Auf weiteren Gedanken

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

ist eigentlich weniger fehleranfällig als

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

So interessant. Idee zu Toolbag hinzufügen ...

1
djna

Im Allgemeinen ist dies eine bewährte Vorgehensweise, aber Sie müssen möglicherweise für Satztypfunktionen den booleschen Typ verwenden, um festzustellen, ob die Operation erfolgreich abgeschlossen wurde oder nicht. Im Allgemeinen gibt es kein Dogma, um zu sagen, dass dies gut oder Bett ist, es kommt natürlich aus der Situation.

1
Narek

Ja, ich finde es eine gute Idee.

Wenn ich etwas hinzufügen könnte, was ist mit diesem Problem:

class People
{
    private String name;
    public People setName(String name)
    {
        this.name = name;
        return this;
    }
}

class Friend extends People
{
    private String nickName;
    public Friend setNickName(String nickName)
    {
        this.nickName = nickName;
        return this;
    }
}

Das wird funktionieren :

new Friend().setNickName("Bart").setName("Barthelemy");

Dies wird von Eclipse nicht akzeptiert! :

new Friend().setName("Barthelemy").setNickName("Bart");

Dies liegt daran, dass setName () ein People und kein Friend zurückgibt und es keinen PeoplesetNickName gibt.

Wie können wir Setter schreiben, um die SELF-Klasse anstelle des Namens der Klasse zurückzugeben?

So etwas wäre in Ordnung (wenn das Schlüsselwort SELF vorhanden wäre). Existiert das überhaupt?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
1
Baptiste

Ich stimme allen Plakaten zu, die behaupten, dass dies die JavaBeans-Spezifikation bricht. Es gibt Gründe, dies zu bewahren, aber ich habe auch das Gefühl, dass die Verwendung dieses Builder-Patterns (auf das verwiesen wurde) seinen Platz hat. Solange es nicht überall verwendet wird, sollte es akzeptabel sein. "It's Place" ist für mich der Endpunkt, wenn eine Methode "build ()" aufgerufen wird.

Es gibt natürlich auch andere Möglichkeiten, all diese Einstellungen festzulegen, aber der Vorteil ist, dass 1) viele Konstruktoren mit vielen Parametern und 2) teilweise spezifizierte Objekte vermieden werden. Hier muss der Builder die benötigten Elemente sammeln und am Ende das Build () aufrufen. Auf diese Weise kann sichergestellt werden, dass kein teilweise angegebenes Objekt erstellt wird, da der Vorgang weniger als öffentlich sichtbar ist. Die Alternative wäre "Parameterobjekte", aber das IMHO schiebt das Problem nur um eine Stufe zurück.

Ich mag viele Konstruktoren mit vielen Parametern nicht, weil sie die Wahrscheinlichkeit haben, dass viele Argumente gleichen Typs übergeben werden, was die Übergabe falscher Argumente an Parameter erleichtert. Ich mag es nicht, viele Setter zu verwenden, weil das Objekt verwendet werden könnte, bevor es vollständig konfiguriert ist. Darüber hinaus ist der Gedanke, Standardwerte basierend auf früheren Auswahlmöglichkeiten zu haben, mit einer "build ()" -Methode besser geeignet.

Kurz gesagt, ich denke, es ist eine gute Praxis, wenn sie richtig angewendet wird.

0
Javaneer

Dies kann weniger lesbar sein

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); 

oder dieses

list.add(new Employee()
          .setName("Jack Sparrow")
          .setId(1)
          .setFoo("bacon!")); 

Dies ist viel lesbarer als:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 
0
Scott

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 eine Frage der Präferenz, und die Verkettung von Setters sieht meiner Meinung nach ziemlich cool aus.

0
Kaiser Keister

Aus der aussage

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

ich sehe zwei Dinge

1) Bedeutungslose Aussage . 2) Fehlende Lesbarkeit.

0
Madhu

Ich mache meine Setter schon seit einiger Zeit und das einzige Problem sind Bibliotheken, die bei den strengen getPropertyDescriptors bleiben, um die Bean-Reader/Writer-Bean-Accessoren zu erhalten. In diesen Fällen hat Ihre Java-Bean nicht die Writer, die Sie erwarten würden. 

Zum Beispiel habe ich es nicht sicher getestet, aber ich wäre nicht überrascht, dass Jackson diese nicht als Setter erkennt, wenn Sie Java-Objekte aus Json/Maps erstellen. Ich hoffe, dass ich mich bei diesem hier falsch liege (ich werde es bald testen).

Tatsächlich entwickle ich ein leichtes SQL-zentriertes ORM, und ich muss den erkannten Setters, die dies zurückgeben, Code Beyong getPropertyDescriptors hinzufügen. 

0
Jeremy Chone

Antwort vor langer Zeit, aber meine zwei Cent ... Es ist in Ordnung. Ich wünschte, diese fließende Schnittstelle würde öfter benutzt.

Durch das Wiederholen der Factory-Variablen werden unten keine weiteren Informationen hinzugefügt:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...

Dies ist sauberer, imho:

ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...

Als eine der bereits erwähnten Antworten müsste die Java-API natürlich angepasst werden, um dies in einigen Situationen, wie Vererbung und Tools, richtig auszuführen.

0
Josef.B

Es ist besser, andere Sprachkonstrukte zu verwenden, falls verfügbar. In Kotlin würden Sie beispielsweise with, apply oder let verwenden. Wenn Sie diesen Ansatz verwenden, brauchen Sie nicht wirklich need, um eine Instanz von Ihrem Setter zurückzugeben.

Bei diesem Ansatz kann Ihr Client-Code folgendermaßen lauten:

  • Gleichgültig wie der Rückgabetyp
  • Einfacher zu warten
  • Vermeiden Sie Nebenwirkungen des Compilers

Hier sind einige Beispiele.

val employee = Employee().apply {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
with(employee) {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
employee.let {
   it.name = "Jack Sparrow"
   it.id = 1
   it.foo = "bacon"
}
0
Steven Spungin