¿Es una mala práctica hacer que un setter devuelva "esto"?

249

¿Es una buena o mala idea hacer que los setters en Java devuelvan "esto"?

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

Este patrón puede ser útil porque puedes encadenar a los setters de esta manera:

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

en lugar de esto:

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

... pero va en contra de la convención estándar. Supongo que podría valer la pena solo porque puede hacer que ese setter haga algo más útil. He visto que este patrón usa algunos lugares (por ejemplo, JMock, JPA), pero parece poco común, y solo se usa generalmente para API muy bien definidas donde este patrón se usa en todas partes.

Actualizar:

Lo que he descrito es obviamente válido, pero lo que realmente estoy buscando es algunas reflexiones sobre si esto es generalmente aceptable y si existen dificultades o prácticas recomendadas relacionadas. Sé sobre el patrón Builder, pero está un poco más involucrado que lo que estoy describiendo, como lo describe Josh Bloch, hay una clase Builder estática asociada para la creación de objetos.

Ken Liu
fuente
1
Como he visto este patrón de diseño hace un tiempo, hago esto siempre que sea posible. Si un método no necesita explícitamente devolver algo para hacer su trabajo, ahora regresa this. A veces, incluso modifico la función para que, en lugar de devolver un valor, funcione en un miembro del objeto, solo para poder hacer esto. Es maravilloso. :)
Inversus
44
Para los setters telescópicos que regresan a sí mismos y en los constructores, prefiero usar withName (String name) en lugar de setName (String name). Como señaló, una práctica común y la expectativa para el setter es devolver el vacío. Los emisores "no estándar" pueden no comportarse bien con los marcos existentes, por ejemplo, los administradores de entidades JPA, Spring, etc.
oᴉɹǝɥɔ
Introduzca saltos de línea antes de cada invocación :) Y configure su IDE u obtenga uno apropiado si no respeta esto.
MauganRa
Los frameworks ampliamente utilizados (Spring e Hibernate, por ejemplo) se adherirán estrictamente (al menos solían hacerlo) a la convención de los que anulan el vacío
Legna

Respuestas:

83

No creo que haya nada específicamente malo en ello, es solo una cuestión de estilo. Es útil cuando:

  • Debe configurar muchos campos a la vez (incluso en la construcción)
  • sabe qué campos debe establecer en el momento en que escribe el código, y
  • Hay muchas combinaciones diferentes para los campos que desea establecer.

Las alternativas a este método pueden ser:

  1. Un megaconstructor (inconveniente: puede pasar muchos valores nulos o predeterminados, y es difícil saber qué valor corresponde a qué)
  2. Varios constructores sobrecargados (inconveniente: se vuelve difícil de manejar una vez que tiene más de unos pocos)
  3. Métodos de fábrica / estáticos (inconveniente: igual que los constructores sobrecargados; se vuelve difícil de manejar una vez que hay más de unos pocos)

Si solo va a establecer algunas propiedades a la vez, diría que no vale la pena devolver 'esto'. Ciertamente se cae si luego decide devolver algo más, como un indicador / mensaje de estado / éxito.

Tom Clift
fuente
1
bueno, por lo general no devuelves nada de un setter, por convención.
Ken Liu el
17
Tal vez no para empezar, pero un setter no necesariamente mantiene su propósito original. Lo que solía ser una variable podría cambiar a un estado que abarca varias variables o tener otros efectos secundarios. Algunos configuradores pueden devolver un valor anterior, otros pueden devolver un indicador de falla si la falla es demasiado común para una excepción. Sin embargo, eso plantea otro punto interesante: ¿qué sucede si una herramienta / marco que está utilizando no reconoce a sus establecedores cuando tienen valores de retorno?
Tom Clift
12
@Tom buen punto, hacer esto rompe la convención "Java Bean" para captadores y establecedores.
Andy White el
2
@TomClift ¿Romper la convención "Java Bean" causa algún problema? Son bibliotecas que usan la convención "Java Bean" mirando el tipo de retorno o solo los parámetros del método y el nombre del método.
Theo Briscoe
3
es por eso que existe el patrón Builder, los setters no deberían devolver algo, en su lugar, crear un constructor si es necesario se ve mejor y requiere menos código :)
RicardoE
106

No es una mala práctica. Es una práctica cada vez más común. La mayoría de los idiomas no requieren que trate con el objeto devuelto si no lo desea, por lo que no cambia la sintaxis de uso del setter "normal" pero le permite encadenar los setters juntos.

Esto se llama comúnmente un patrón de construcción o una interfaz fluida .

También es común en la API de Java:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
cletus
fuente
26
A menudo se usa en constructores, pero no diría "esto se llama ... patrón de constructor".
Laurence Gonsalves
10
Es divertido para mí que parte de la lógica de las interfaces fluidas es que hacen que el código sea más fácil de leer. Pude ver que era más conveniente escribir, pero me parece más difícil de leer. Ese es el único desacuerdo real que tengo con él.
Brent escribe el código el
30
También se conoce como antipatrón de choque de trenes. El problema es que cuando una traza de pila de excepción de puntero nulo contiene una línea como esta, no tiene idea de qué invocación devuelve nulo. Eso no quiere decir que el encadenamiento deba evitarse a toda costa, pero tenga cuidado con las bibliotecas defectuosas (especialmente en casa).
ddimitrov
18
@ddimitrov mientras lo limites a devolver esto , nunca sería un problema (solo la primera invocación podría arrojar NPE)
Stefan
44
¡es más fácil escribir Y leer, suponiendo que ponga saltos de línea e sangría donde la legibilidad se vería afectada de otra manera! (porque evita el desorden redundante de código repetitivo como bla.foo.setBar1(...) ; bla.foo.setBar2(...)cuando podría escribir bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(no puede usar saltos de línea en un comentario SO como este :-( ... espero que obtenga el punto teniendo en cuenta 10 de esos setters o llamadas más complejas)
Andreas Dietrich
90

Para resumir:

  • se llama "interfaz fluida" o "encadenamiento de métodos".
  • esto no es Java "estándar", aunque lo ves más y más en estos días (funciona muy bien en jQuery)
  • viola la especificación JavaBean, por lo que romperá con varias herramientas y bibliotecas, especialmente los constructores JSP y Spring.
  • puede evitar algunas optimizaciones que la JVM normalmente haría
  • algunas personas piensan que limpia el código, otras piensan que es "horrible"

Un par de otros puntos no mencionados:

  • Esto viola el principio de que cada función debe hacer una (y solo una) cosa. Puede o no creer en esto, pero en Java creo que funciona bien.

  • Los IDEs no los van a generar (por defecto).

  • Finalmente, aquí hay un punto de datos del mundo real. He tenido problemas al usar una biblioteca construida así. El generador de consultas de Hibernate es un ejemplo de esto en una biblioteca existente. Dado que los métodos set * de Query devuelven consultas, es imposible saber con solo mirar la firma cómo usarlo. Por ejemplo:

    Query setWhatever(String what);
  • Introduce una ambigüedad: el método modifica el objeto actual (su patrón) o, quizás, la consulta es realmente inmutable (un patrón muy popular y valioso), y el método está devolviendo uno nuevo. Simplemente hace que la biblioteca sea más difícil de usar, y muchos programadores no explotan esta característica. Si los setters fueran setters, sería más claro cómo usarlo.

ndp
fuente
55
por cierto, es "fluido", no "fluido" ... ya que le permite estructurar una serie de llamadas a métodos como un idioma hablado.
Ken Liu el
1
El último punto sobre la inmutabilidad es muy importante. El ejemplo más simple es String. Los desarrolladores de Java esperan que cuando usen métodos en String obtengan una instancia totalmente nueva y no la misma instancia modificada. Con una interfaz fluida, debería mencionarse en la documentación del método que el objeto devuelto es 'esto' en lugar de una nueva instancia.
MeTTeO
77
Aunque estoy de acuerdo en general, no estoy de acuerdo con que esto viole el principio de "hacer una sola cosa". Volver thisno es una ciencia compleja de cohetes. :-)
user949300
punto adicional: también viola el principio de separación Comando-consulta.
Marton_hun
84

Prefiero usar métodos 'con' para esto:

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

Así:

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

Advertencia : esta withXsintaxis se usa comúnmente para proporcionar "establecedores" para objetos inmutables, por lo que las personas que llaman de estos métodos pueden esperar razonablemente que creen nuevos objetos en lugar de mutar la instancia existente. Tal vez una redacción más razonable sería algo como:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

Con la convención de nombres chainsetXyz (), prácticamente todos deberían estar contentos.

qualidafial
fuente
18
+1 Para convenciones interesantes. No voy a adoptarlo en mi propio código, ya que parece que ahora debes tener un get, a sety a withpara cada campo de clase. Sin embargo, sigue siendo una solución interesante. :)
Paul Manta
1
Depende de con qué frecuencia llame a los setters. Descubrí que si se llama mucho a esos setters, vale la pena agregarlos ya que simplifica el código en cualquier otro lugar. YMMV
qualidafial
2
Y si agregaste esto a Project Lomboklas @Getter/@Setteranotaciones ... eso sería fantástico para encadenar. O podría usar algo muy parecido al Kestrelcombinador ( github.com/raganwald/Katy ) que usan los demonios JQuery y Javascript.
Ehtesh Choudhury
44
@ AlikElzin-kilaka En realidad, acabo de notar que las clases inmutables java.time en Java 8 usan este patrón, por ejemplo, LocalDate.withMonth, withYear, etc.
calidad
44
El withprefijo es una convención diferente. como @qualidafial dio un ejemplo. los métodos con el prefijo withno deberían regresar, thissino una nueva instancia como la instancia actual, pero withese cambio. Esto se hace cuando desea que sus objetos sean inmutables. Entonces, cuando veo un método con el prefijo withAsumo que obtendré un nuevo objeto, no el mismo objeto.
tempcke
26

Si no desea regresar 'this'del configurador pero no desea usar la segunda opción, puede usar la siguiente sintaxis para establecer las propiedades:

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

Como comentario aparte, creo que es un poco más limpio en C #:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
Luke Quinane
fuente
16
la inicialización de doble paréntesis puede tener problemas con iguales porque crea una clase interna anónima; ver c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H
@Csaba_H Claramente, ese problema es culpa de la persona que arruinó el equalsmétodo. Hay formas muy claras de tratar con clases anónimas equalssi sabes lo que estás haciendo.
AJMansfield
creando una nueva clase (anónima) solo por eso? para cada instancia?
user85421
11

No solo rompe la convención de getters / setters, también rompe el marco de referencia del método Java 8. MyClass::setMyValuees un BiConsumer<MyClass,MyValue>, y myInstance::setMyValuees un Consumer<MyValue>. Si le devuelve su setter this, entonces ya no es una instancia válida Consumer<MyValue>, sino una Function<MyValue,MyClass>, y causará que se rompa cualquier cosa que use referencias de métodos a esos setters (suponiendo que sean métodos nulos).

Steve K
fuente
2
Sería increíble si Java tuviera alguna forma de sobrecargar por tipo de retorno, no solo la JVM. Podría evitar muchos de estos cambios importantes fácilmente.
Adowrath
1
Siempre puede definir una interfaz funcional que extienda ambos Consumer<A>y Function<A,B>proporcionando una implementación predeterminada de void accept(A a) { apply(a); }. Entonces se puede usar fácilmente como uno y no romperá ningún código que requiera una forma específica.
Steve K
Argh! ¡Esto esta simplemente mal! Esto se llama compatibilidad nula. Un setter que devuelve un valor puede actuar como un consumidor. ideone.com/ZIDy2M
Michael
8

No conozco Java, pero lo hice en C ++. Otras personas han dicho que hace que las líneas sean muy largas y difíciles de leer, pero lo he hecho así muchas veces:

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

Esto es aún mejor:

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

al menos eso creo. Pero puedes invitarme a votar y llamarme un programador horrible si lo deseas. Y no sé si puedes hacer esto en Java.

Carson Myers
fuente
Lo "aún mejor" no se presta bien a la funcionalidad de formato de código fuente disponible en los IDE modernos. Desafortunadamente.
Thorbjørn Ravn Andersen
probablemente tenga razón ... El único formateador automático que he usado es la sangría automática de emacs.
Carson Myers
2
Los formateadores de código fuente pueden coaccionarse con un simple // después de cada llamada a método en la cadena. Mejora un poco su código, pero no tanto tener su serie vertical de declaraciones reformateadas horizontalmente.
qualidafial
@qualidafial No necesitas // después de cada método si configura su IDE para que no se una a las líneas ya ajustadas (por ejemplo, Eclipse> Propiedades> Java> Estilo de código> Formateador> Ajuste de línea> Nunca unir líneas ya ajustadas).
DJDaveMark
6

Este esquema (juego de palabras), llamado 'interfaz fluida', se está volviendo bastante popular ahora. Es aceptable, pero en realidad no es mi taza de té.

Seda del mediodía
fuente
6

Como no devuelve nulo, ya no es un configurador de propiedades JavaBean válido. Eso podría importar si eres una de las siete personas en el mundo que usa herramientas visuales "Bean Builder", o una de las 17 que usan elementos JSP-bean-setProperty.

Conocido
fuente
También es importante si utiliza marcos con reconocimiento de bean como Spring.
ddimitrov
6

Al menos en teoría , puede dañar los mecanismos de optimización de la JVM al establecer dependencias falsas entre llamadas.

Se supone que es azúcar sintáctica, pero de hecho puede crear efectos secundarios en la máquina virtual de Java 43 súper inteligente.

Por eso voto no, no lo use.

Mariano
fuente
10
Interesante ... ¿podría ampliar esto un poco?
Ken Liu el
3
Solo piense en cómo los procesadores superescalares manejan la ejecución paralela. El objeto para ejecutar el segundo setmétodo depende del primer setmétodo, aunque lo conoce el programador.
Marian
2
Todavía no te sigo. Si configura Foo y luego Bar con dos declaraciones separadas, el objeto para el que está configurando Bar tiene un estado diferente que el objeto para el que está configurando Foo. Por lo tanto, el compilador tampoco podría paralelizar esas declaraciones. Al menos, no veo cómo podría hacerlo sin introducir una suposición injustificada. (Como no tengo idea al respecto, no negaré que Java 43 de hecho hace la paralelización en un caso, pero no en el otro e introduzco la suposición injustificada en un caso pero no en el otro).
masonk
12
Si no lo sabes, prueba. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining El java7 jdk definitivamente integra métodos encadenados, y realiza alrededor del mismo número de iteraciones que se necesitan para marcar los establecedores de vacíos como activos y también en línea. Creo que subestimas el poder de los algoritmos de poda de código de operación de JVM; si sabe que está devolviendo esto, omitirá el código de operación jrs (declaración de devolución de Java) y simplemente lo dejará en la pila.
Ajax
1
Andreas, estoy de acuerdo, pero los problemas aparecen cuando tienes capa sobre capa de código ineficiente. El 99% del tiempo debe codificar para obtener claridad, lo que se dice mucho por aquí. Pero también hay momentos en los que necesita ser realista y usar sus años de experiencia para optimizar prematuramente en un sentido arquitectónico general.
LegendLength
6

No es una mala práctica en absoluto. Pero no es compatible con JavaBeans Spec .

Y hay muchas especificaciones que dependen de esos accesores estándar.

Siempre puedes hacer que coexistan entre sí.

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

Ahora podemos usarlos juntos.

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

Aquí viene otra versión para objeto inmutable.

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

Ahora podemos hacer esto.

new Some.Builder().value("value").build().getValue();
Jin Kwon
fuente
2
Mi edición fue rechazada, pero su ejemplo de Builder no es correcto. Primero, .value () no devuelve nada, y ni siquiera establece el somecampo. En segundo lugar, debe agregar una salvaguardia y el conjunto somede nullde acumulación () por lo que Somees verdaderamente inmutable, de lo contrario se podría llamar builder.value()en la misma instancia del constructor de nuevo. Y, por último, sí, tiene un generador, pero Sometodavía tiene un constructor público, lo que significa que no defiende abiertamente el uso del generador, es decir, el usuario no lo sabe más que probar o buscar un método para establecer un método personalizado valueen absoluto.
Adowrath
@Adowrath si la respuesta es incorrecta, debe escribir su propia respuesta, no tratar de editar la forma de otra persona
CalvT
1
@JinKwon Genial. ¡Gracias! Y perdón si antes me parecía grosero.
Adowrath
1
@Adowrath Por favor, siéntase libre de comentarios adicionales para mejoras. Para su información, no fui yo quien rechazó su edición. :)
Jin Kwon
1
Sé que sé. ^^ Y gracias por la nueva versión, que ahora proporciona un verdadero constructor mutable "Immutable-Some". Y es una solución más inteligente que mis intentos de edición que, en comparación, desordenaron el código.
Adowrath
4

Si utiliza la misma convención en toda la aplicación, parece estar bien.

Por otro lado, si una parte existente de su aplicación usa una convención estándar, me apegaría a ella y agregaría constructores a clases más complicadas

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;
    }
}
Marcin Szymczak
fuente
1
Esto es exactamente para lo que se diseñó el patrón Builder. No rompe lambdas en Java 8, no rompe las delicadas herramientas JavaBeans y no causa problemas de optimización en JVM (ya que el objeto solo existe durante la creación de instancias). También resuelve el problema de "demasiados constructores" que se obtiene simplemente al no usar constructores, así como al eliminar la contaminación del montón de las clases anónimas de doble paréntesis.
ndm13
Punto interesante: sin embargo, me doy cuenta de que si casi nada en su clase es inmutable (por ejemplo, una GUI altamente configurable), entonces probablemente pueda evitar Builder por completo.
Philip Guin
4

Paulo Abrantes ofrece otra forma de hacer que los configuradores de JavaBean sean fluidos: defina una clase de generador interno para cada JavaBean. Si está utilizando herramientas que se desconciertan por los setters que devuelven valores, el patrón de Paulo podría ayudar.

Jim Ferrans
fuente
2
@ cdunn2001, usa la máquina Wayback, Luke
msangel
3

Estoy a favor de que los setters vuelvan "esto". No me importa si no es compatible con los frijoles. Para mí, si está bien tener la expresión / declaración "=", entonces los establecedores que devuelven valores están bien.

Monty Hall
fuente
2

Solía ​​preferir este enfoque, pero he decidido no hacerlo.

Razones:

  • Legibilidad. Hace que el código sea más legible para tener cada setFoo () en una línea separada. Por lo general, lee el código muchas, muchas veces más que la única vez que lo escribe.
  • Efecto secundario: setFoo () solo debe establecer field foo, nada más. Devolver esto es un extra "QUÉ fue eso".

El patrón Builder que vi no usa la convención setFoo (foo) .setBar (bar) sino más foo (foo) .bar (bar). Quizás por exactamente esas razones.

Es, como siempre, una cuestión de gustos. Simplemente me gusta el enfoque de "menos sorpresas".

Thorbjørn Ravn Andersen
fuente
2
Estoy de acuerdo con el efecto secundario. Los setters que devuelven cosas violan su nombre. ¿Estás configurando foo, pero recuperas un objeto? ¿Es este un objeto nuevo o he alterado el viejo?
crunchdog
2

Este patrón particular se llama Método de encadenamiento. Enlace de Wikipedia , esto tiene más explicaciones y ejemplos de cómo se hace en varios lenguajes de programación.

PD: Solo pensé en dejarlo aquí, ya que estaba buscando el nombre específico.

capt.swag
fuente
1

A primera vista: "¡Horrible!".

Pensándolo mejor

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

en realidad es menos propenso a errores que

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

Muy interesante Añadiendo idea a la bolsa de herramientas ...

djna
fuente
1
No, todavía es horrible. Desde una perspectiva de mantenimiento, este último es mejor porque es más fácil de leer. Además, los verificadores de código automatizados como CheckStyle impondrán líneas de hasta 80 caracteres de forma predeterminada: el código se envolvería de todos modos, lo que agravaría el problema de legibilidad / mantenimiento. Y finalmente, es Java; No hay ningún beneficio en escribir todo en una sola línea cuando se va a compilar en código de bytes de todos modos.
OMG Ponis
1
personalmente, creo que lo primero es más fácil de leer, especialmente si está creando varios objetos de esta manera.
Ken Liu el
@ Ken: codifique un método. Escriba una copia en formato fluido; otra copia en la otra. Ahora entregue las dos copias a un par de personas y pregunte cuál les resulta más fácil de leer. Más rápido de leer, más rápido de codificar.
OMG Ponis
Como la mayoría de las herramientas, puede ser fácil de usar en exceso. JQuery está orientado en torno a esta técnica y, por lo tanto, es propenso a largas cadenas de llamadas que, en mi opinión, perjudica la legibilidad.
staticsan
2
Estaría bien si hubiera saltos de línea antes de cada punto y sangrado. Entonces, sería tan legible como la segunda versión. En realidad más, porque no hay redundancia listal principio.
MauganRa
1

Sí, creo que es una buena idea.

Si pudiera agregar algo, ¿qué pasa con este 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;
    }
}

Esto funcionará:

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

¡Eclipse no lo aceptará! :

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

Esto se debe a que setName () devuelve un People y no un Friend, y no hay PeoplesetNickName.

¿Cómo podríamos escribir setters para devolver la clase SELF en lugar del nombre de la clase?

Algo como esto estaría bien (si existiera la palabra clave SELF). ¿Existe esto de todos modos?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
Baptiste
fuente
1
Hay algunos otros compiladores de Java además de Eclipse que no aceptarán eso :). Básicamente, está ejecutando aves del sistema de tipos Java (que es estático, no dinámico como algunos lenguajes de scripting): tendrá que emitir lo que sale de setName () a un amigo antes de que pueda establecerNickName () en él. Entonces, desafortunadamente, para las jerarquías de herencia esto elimina gran parte de la ventaja de legibilidad y hace que esta técnica potencialmente útil no sea tan útil.
Cornel Masson
55
Usa genéricos. clase Encadenable <Self extiende Encadenable> {Public Self doSomething () {return (Self) this;}} No es técnicamente seguro de tipo (lanzará la clase si implementa la clase con un tipo Self que no se puede utilizar), pero es la gramática correcta, y las subclases devolverán su propio tipo.
Ajax
Además: Este "SER" que Baptiste está usando se llama auto-tipo, no está presente en muchos idiomas (Scala es realmente el único que puedo pensar en este momento), donde el uso de Generics de Ajax es el llamado "Curiosamente patrón de plantilla recurrente ", que trata de hacer frente a la falta del auto tipo, pero tiene algunos inconvenientes: (de class C<S extends C<S>>, que es más seguro que un plano S extends C. a) Si A extends B, y B extends C<B>, y tiene una A, solo sabe que un B se devuelve a menos que A anule todos y cada uno de los métodos. b) No puede denotar un local, no crudo C<C<C<C<C<...>>>>>.
Adowrath
1

En general, es una buena práctica, pero es posible que necesite que las funciones de tipo conjunto usen el tipo booleano para determinar si la operación se completó con éxito o no, esa también es una forma. En general, no hay dogma para decir que esto es bueno o la cama, proviene de la situación, por supuesto.

Narek
fuente
2
¿Qué tal usar excepciones para indicar una condición de error? Los códigos de error pueden ignorarse fácilmente como muchos programadores de C han aprendido dolorosamente. Las excepciones pueden hacer burbujear la pila hasta el punto donde se pueden manejar.
ddimitrov
Por lo general, las excepciones son preferibles, pero los códigos de error también son útiles cuando no se pueden usar excepciones.
Narek
1
Hola @Narek, quizás podrías explicar en qué casos es preferible usar códigos de error en los setters, en lugar de excepciones y ¿por qué?
ddimitrov
0

De la declaración

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

estoy viendo dos cosas

1) Declaración sin sentido. 2) Falta de legibilidad.

Madhu
fuente
0

Esto puede ser menos legible

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

o esto

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

Esto es mucho más legible que:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 
Scott
fuente
8
Creo que es bastante legible si no intentas poner todo tu código en una línea.
Ken Liu
0

He estado haciendo mis setters durante bastante tiempo y el único problema real es con las bibliotecas que se adhieren a los estrictos getPropertyDescriptors para obtener los accesos de bean bean reader / writer. En esos casos, su "bean" de Java no tendrá los escritores que usted esperaría.

Por ejemplo, no lo he probado con seguridad, pero no me sorprendería que Jackson no los reconozca como setters al crear objetos java a partir de json / maps. Espero estar equivocado en este caso (lo probaré pronto).

De hecho, estoy desarrollando un ORM ligero centrado en SQL y tengo que agregar algo de código entre getPropertyDescriptors a los creadores reconocidos que devuelven esto.

Jeremy Chone
fuente
0

Hace mucho tiempo respondí, pero mis dos centavos ... Está bien. Desearía que esta interfaz fluida se usara con más frecuencia.

La repetición de la variable 'factory' no agrega más información a continuación:

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

Esto es más limpio, en mi humilde opinión:

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

Por supuesto, como una de las respuestas ya mencionadas, la API de Java tendría que modificarse para hacer esto correctamente en algunas situaciones, como la herencia y las herramientas.

Josef.B
fuente
0

Es mejor usar otras construcciones de lenguaje si están disponibles. Por ejemplo, en Kotlin, usaría con , aplicaría o dejaría . Si usa este enfoque, realmente no necesitará devolver una instancia de su configurador.

Este enfoque permite que su código de cliente sea:

  • Indiferente al tipo de retorno
  • Más fácil de mantener.
  • Evitar los efectos secundarios del compilador

Aquí hay unos ejemplos.

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"
}
Steven Spungin
fuente
0

Si estoy escribiendo una API, uso "devolver esto" para establecer valores que solo se establecerán una vez. Si tengo otros valores que el usuario debería poder cambiar, en su lugar uso un setter vacío estándar.

Sin embargo, es realmente una cuestión de preferencia y encadenar a los setters se ve bastante bien, en mi opinión.

Kaiser Keister
fuente
0

Estoy de acuerdo con todos los carteles que afirman que esto rompe las especificaciones de JavaBeans. Hay razones para preservar eso, pero también siento que el uso de este Patrón de Constructor (al que se aludió) tiene su lugar; siempre que no se use en todas partes, debería ser aceptable. "Es lugar", para mí, es donde el punto final es una llamada a un método "build ()".

Por supuesto, hay otras formas de establecer todas estas cosas, pero la ventaja aquí es que evita 1) constructores públicos de muchos parámetros y 2) objetos parcialmente especificados. Aquí, el constructor recopila lo que se necesita y luego llama a su "build ()" al final, lo que puede garantizar que no se construya un objeto parcialmente especificado, ya que esa operación puede tener una visibilidad inferior a la pública. La alternativa sería "objetos de parámetros", pero esa IMHO simplemente empuja el problema un nivel atrás.

No me gustan los constructores de muchos parámetros porque hacen que sea más probable que se pasen muchos argumentos del mismo tipo, lo que puede facilitar la transmisión de argumentos incorrectos a los parámetros. No me gusta usar muchos setters porque el objeto podría usarse antes de que esté completamente configurado. Además, la noción de tener valores predeterminados basados ​​en elecciones anteriores se sirve mejor con un método "build ()".

En resumen, creo que es una buena práctica, si se usa correctamente.

Javaneer
fuente
-4

Mal hábito: un setter establece un getter get

¿Qué hay de declarar explícitamente un método, que lo hace para U

setPropertyFromParams(array $hashParamList) { ... }
Ulrich Tevi Horus
fuente
no es bueno para el autocompletado y la refactorización y la legibilidad anb código repetitivo
Andreas Dietrich
Porque: 1) debe recordar el orden o leerlo de los documentos, 2) pierde toda la seguridad de tipo de tiempo de compilación, 3) tiene que emitir los valores (esto y 2) no es un problema en una dinámica idioma, por supuesto), 4) puede no extender esta manera útil que no sea el control de la matriz de longitud en cada llamada, y 5) si se cambia nada, ya se trate de un pedido o incluso la existencia de ciertos valores, puede no comprobar la ausencia de tiempo de ejecución ni compilar timewithout duda en absoluto . Con setters: 1), 2), 3): no es un problema. 4) Agregar nuevos métodos no rompe nada. 5) mensaje de error explícito.
Adowrath