È una ctriggers pratica fare in modo che un setter ritorni “questo”?

È una buona o una ctriggers idea che i setter di java ritornino “questo”?

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

Questo schema può essere utile perché in questo modo è ansible incatenare i setter in questo modo:

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

Invece di questo:

 Employee e = new Employee(); e.setName("Jack Sparrow"); ...and so on... list.add(e); 

… ma va contro la convenzione standard. Suppongo che potrebbe valerne la pena solo perché può fare in modo che quel setter faccia qualcos’altro utile. Ho visto questo schema utilizzato in alcuni punti (ad esempio JMock, JPA), ma sembra non comune, e solo in genere viene utilizzato per API molto ben definite in cui questo schema viene utilizzato ovunque.

Aggiornare:

Quello che ho descritto è ovviamente valido, ma quello che cerco davvero è qualche idea sul fatto che sia generalmente accettabile, e se ci sono insidie ​​o buone pratiche correlate. Conosco il pattern Builder ma è un po ‘più coinvolto di ciò che sto descrivendo – come Josh Bloch descrive che esiste una class Builder statica associata per la creazione di oggetti.

Non penso che ci sia qualcosa di specificamente sbagliato in questo, è solo una questione di stile. È utile quando:

  • È necessario impostare molti campi contemporaneamente (incluso alla costruzione)
  • sai quali campi devi impostare nel momento in cui stai scrivendo il codice e
  • ci sono molte combinazioni diverse per i campi che vuoi impostare.

Alternative a questo metodo potrebbero essere:

  1. Un mega costruttore (lato negativo: potresti passare molti valori nulli o valori predefiniti, e diventa difficile sapere quale valore corrisponde a cosa)
  2. Diversi costruttori sovraccaricati (lato negativo: diventa ingombrante una volta che ne hai più di pochi)
  3. Metodi di fabbrica / statici (lato negativo: come i costruttori sovraccaricati – diventano ingombranti una volta che ce ne sono più di pochi)

Se hai intenzione di impostare solo alcune proprietà alla volta, direi che non vale la pena restituire “questo”. Cade senz’altro se in seguito si decide di restituire qualcos’altro, come uno stato / indicatore / messaggio di successo.

Non è una ctriggers pratica. È una pratica sempre più comune. La maggior parte delle lingue non richiede che tu gestisca l’object restituito se non lo desideri, in modo che non cambi la syntax di utilizzo “normale” setter ma ti consenta di mettere insieme i setter.

Questo è comunemente chiamato un modello di builder o un’interfaccia fluente .

È anche comune nell’API Java:

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

Preferisco usare i metodi “con” per questo:

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

Così:

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

Riassumere:

  • si chiama “interfaccia fluente” o “concatenamento del metodo”.
  • questo non è Java “standard”, anche se lo vedi più in questi giorni (funziona alla grande in jQuery)
  • viola le specifiche JavaBean, quindi si romperà con vari strumenti e librerie, in particolare con i costruttori JSP e Spring.
  • potrebbe impedire alcune ottimizzazioni che la JVM normalmente farebbe
  • alcune persone pensano che pulisca il codice, altri pensano che sia “orribile”

Un paio di altri punti non menzionati:

  • Questo viola il principio che ogni funzione dovrebbe fare una (e solo una) cosa. Puoi o non puoi crederci, ma in Java credo che funzioni bene.

  • Gli IDE non genereranno questi per te (di default).

  • Finalmente, ecco un punto dati del mondo reale. Ho avuto problemi nell’uso di una libreria costruita in questo modo. Il generatore di query di Hibernate ne è un esempio in una libreria esistente. Poiché i metodi set di Query * stanno restituendo query, è imansible dirlo semplicemente osservando la firma come usarlo. Per esempio:

     Query setWhatever(String what); 
  • Introduce un’ambiguità: il metodo modifica l’object corrente (il tuo pattern) o, forse, Query è davvero immutabile (un pattern molto popolare e prezioso) e il metodo ne sta restituendo uno nuovo. Rende solo la libreria più difficile da utilizzare e molti programmatori non sfruttano questa funzionalità. Se setter fossero setter, sarebbe più chiaro come usarlo.

Se non vuoi restituire 'this' dal setter ma non vuoi usare la seconda opzione puoi usare la seguente syntax per impostare le proprietà:

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

Per inciso penso che sia leggermente più pulito in C #:

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

Non solo rompe la convenzione di getter / setter, ma infrange anche il framework di riferimento del metodo Java 8. MyClass::setMyValue è un BiConsumer e myInstance::setMyValue è un Consumer . Se il tuo setter restituisce this , allora non è più un’istanza valida di Consumer , ma piuttosto una Function , e causerà qualsiasi cosa usando i riferimenti al metodo a questi setter (supponendo che siano metodi void) per interrompere .

Non conosco Java ma l’ho fatto in C ++. Altre persone hanno detto che rende le righe davvero lunghe e davvero difficili da leggere, ma l’ho fatto così tante volte:

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

Questo è ancora meglio:

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

almeno, penso. Ma tu sei il benvenuto a farmi una visita e chiamarmi un programmatore tremendo, se lo desideri. E non so se ti sia permesso di farlo anche in Java.

Poiché non restituisce nulla, non è più un setter di proprietà JavaBean valido. Potrebbe essere importante se sei una delle sette persone nel mondo che usa strumenti visivi “Bean Builder” o uno dei 17 che usa elementi JSP-bean-setProperty.

Non è affatto una ctriggers pratica. Ma non è compatibile con JavaBeans Spec .

E ci sono molte specifiche che dipendono da quegli accessor standard.

Puoi sempre farli coesistere l’uno con l’altro.

 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; } 

Ora possiamo usarli insieme.

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

Ecco un’altra versione per l’object immutabile.

 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; } 

Ora possiamo farlo.

 new Some.Builder().value("value").build().getValue(); 

Questo schema (gioco di parole), chiamato “interfaccia fluente”, sta diventando molto popolare ora. È accettabile, ma non è davvero la mia tazza di tè.

Almeno in teoria , può danneggiare i meccanismi di ottimizzazione della JVM impostando false dipendenze tra le chiamate.

Dovrebbe essere zucchero sintattico, ma in effetti può creare effetti collaterali nella macchina virtuale super-intelligente di Java 43.

Ecco perché io voto no, non lo uso.

Sono a favore dei setter che hanno “questo” ritorno. Non mi interessa se non è conforms ai fagioli. Per me, se va bene avere l’espressione / istruzione “=”, allora i setter che restituiscono valori vanno bene.

Preferivo questo approccio ma ho deciso contro.

Motivi:

  • Leggibilità. Rende il codice più leggibile per avere setFoo () su una riga separata. Di solito leggi il codice molte, molte più volte della singola volta che lo scrivi.
  • Effetto collaterale: setFoo () dovrebbe solo impostare il campo foo, nient’altro. Restituire questo è un extra “COSA era quello”.

Il pattern Builder che ho visto non usa la convenzione setFoo (foo) .setBar (barra) ma più foo (foo) .bar (barra). Forse proprio per queste ragioni.

È, come sempre, una questione di gusti. Mi piace l’approccio “meno sorprese”.

Questo particolare modello è chiamato metodo concatenamento. Link a Wikipedia , questo ha più spiegazioni ed esempi di come è fatto in vari linguaggi di programmazione.

PS: Ho pensato di lasciarlo qui, dato che stavo cercando il nome specifico.

Se si utilizza la stessa convenzione in tutta l’applicazione sembra soddisfacente.

D’altra parte, se una parte esistente dell’applicazione utilizza la convenzione standard, mi attenersi ad essa e aggiungere i builder a classi più complicate

 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; } } 

Paulo Abrantes offre un altro modo per rendere fluenti i setter JavaBean: definire una class builder interna per ciascun JavaBean. Se stai usando strumenti che vengono sconcertati da setter che restituiscono valori, lo schema di Paulo potrebbe essere d’aiuto.

A prima vista: “orribile!”.

Su ulteriore pensiero

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

è in realtà meno incline agli errori

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

Quindi abbastanza interessante. Aggiungere un’idea alla borsa degli attrezzi …

Sì, penso che sia una buona idea.

Se potessi aggiungere qualcosa, per quanto riguarda questo problema:

 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; } } 

Questo funzionerà:

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

Questo non sarà accettato da Eclipse! :

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

Questo perché setName () restituisce un Popolo e non un Amico, e non vi è alcun PeoplesetNickName.

Come possiamo scrivere setter per restituire la class SELF al posto del nome della class?

Qualcosa di simile andrebbe bene (se esistesse la parola chiave SELF). Questo esiste comunque?

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

In generale è una buona pratica, ma potrebbe essere necessario per le funzioni di tipo set utilizzare il tipo booleano per determinare se l’operazione è stata completata correttamente o meno, anche questo è un modo. In generale, non esiste dogma per dire che questo è buono o letto, viene dalla situazione, ovviamente.

Dalla dichiarazione

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

sto vedendo due cose

1) affermazione senza senso. 2) Mancanza di leggibilità.

Questo potrebbe essere meno leggibile

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

o questo

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

Questo è molto più leggibile di:

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

Ho fatto i miei setter per un po ‘di tempo e l’unico vero problema è con le librerie che si attaccano ai rigorosi getPropertyDescriptors per ottenere gli accessori bean bean reader / writer. In questi casi, il tuo “bean” java non avrà le parti che ti aspetteresti.

Ad esempio, non l’ho provato con certezza, ma non sarei sorpreso che Jackson non riconoscesse quelli come setter quando creano oggetti java da json / maps. Spero di sbagliarmi su questo (lo proverò presto).

Infatti, sto sviluppando un ORM SQL leggero e devo aggiungere qualche codice oltre a getPropertyDescriptors ai setter riconosciuti che restituiscono questo.

Molto tempo fa rispondi, ma i miei due centesimi … Va bene. Vorrei che questa interfaccia fluente fosse usata più spesso.

La ripetizione della variabile “factory” non aggiunge ulteriori informazioni di seguito:

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

Questo è più pulito, imho:

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

Naturalmente, come una delle risposte già menzionate, l’API Java dovrebbe essere ottimizzata per farlo correttamente per alcune situazioni, come l’ereditarietà e gli strumenti.

È preferibile utilizzare altri costrutti di linguaggio, se disponibili. Ad esempio, in Kotlin, dovresti usare, applicare o lasciare . Se utilizzi questo approccio, non avrai davvero bisogno di restituire un’istanza dal tuo setter.

Questo approccio consente al tuo codice cliente di essere:

  • Indifferente al tipo di ritorno
  • Più semplice da mantenere
  • Evita gli effetti collaterali del compilatore

Ecco alcuni esempi.

 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" } 

Se sto scrivendo un’API, io uso “restituisci questo” per impostare valori che verranno impostati solo una volta. Se ho altri valori che l’utente dovrebbe essere in grado di cambiare, uso invece un setter standard per il void.

Tuttavia, è davvero una questione di preferenza e gli incastonatori settantini sono piuttosto interessanti, secondo me.

Sono d’accordo con tutti i poster che sostengono che ciò infrange le specifiche JavaBeans. Ci sono dei motivi per preservarlo, ma sento anche che l’uso di questo Pattern Builder (a cui si è fatto riferimento) ha il suo posto; finché non viene usato ovunque, dovrebbe essere accettabile. “It’s Place”, per me, è dove il punto finale è una chiamata a un metodo “build ()”.

Ci sono altri modi per impostare tutte queste cose, naturalmente, ma il vantaggio qui è che evita 1) costruttori pubblici con molti parametri e 2) oggetti parzialmente specificati. Qui, hai il builder che raccoglie ciò che è necessario e poi chiama il suo “build ()” alla fine, che può quindi garantire che un object parzialmente specificato non sia costruito, poiché a tale operazione può essere fornita una visibilità inferiore a quella pubblica. L’alternativa sarebbe “oggetti parametro”, ma IMHO semplicemente riporta il problema a un livello.

Non mi piacciono i costruttori a molti parametri perché rendono più probabile l’invio di molti argomenti dello stesso tipo, il che può rendere più semplice passare gli argomenti sbagliati ai parametri. Non mi piace usare molti setter perché l’object potrebbe essere usato prima che sia completamente configurato. Inoltre, la nozione di avere valori predefiniti basati su scelte precedenti è meglio servita con un metodo “build ()”.

In breve, penso che sia una buona pratica, se usata correttamente.

Ctriggers abitudine: un setter imposta un get get

che dire di dichiarare esplicitamente un metodo, che lo fa per U

 setPropertyFromParams(array $hashParamList) { ... }