Tener encadenado implementado en beans es muy útil: no hay necesidad de sobrecargar a los constructores, megaconstructores, fábricas y le brinda una mayor legibilidad. No puedo pensar en ningún inconveniente, a menos que desee que su objeto sea inmutable , en cuyo caso no tendría ningún setter de todos modos. Entonces, ¿hay alguna razón por la cual esta no es una convención OOP?
public class DTO {
private String foo;
private String bar;
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public DTO setFoo(String foo) {
this.foo = foo;
return this;
}
public DTO setBar(String bar) {
this.bar = bar;
return this;
}
}
//...//
DTO dto = new DTO().setFoo("foo").setBar("bar");
myCustomDTO = DTOBuilder.defaultDTO().withFoo("foo").withBar("bar").Build();
Lo haría para no entrar en conflicto con la idea general de que los colocadores son vacíos.new Foo().setBar('bar').setBaz('baz')
siente muy "fluido". Quiero decir, seguro que podría implementarse exactamente de la misma manera, pero esperaba leer algo más comoFoo().barsThe('bar').withThe('baz').andQuuxes('the quux')
Respuestas:
Mi mejor suposición: porque viola CQS
Tienes un comando (que cambia el estado del objeto) y una consulta (que devuelve una copia de estado, en este caso, el objeto mismo) mezclado en el mismo método. Eso no es necesariamente un problema, pero viola algunas de las pautas básicas.
Por ejemplo, en C ++, std :: stack :: pop () es un comando que devuelve void, y std :: stack :: top () es una consulta que devuelve una referencia al elemento superior en la pila. Clásicamente, te gustaría combinar los dos, pero no puedes hacer eso y estar a salvo de excepciones. (No es un problema en Java, porque el operador de asignación en Java no arroja).
Si DTO fuera un tipo de valor, podría lograr un final similar con
Además, encadenar los valores de retorno es un dolor colosal cuando se trata de herencia. Consulte el "Patrón de plantilla curiosamente recurrente"
Finalmente, está el problema de que el constructor predeterminado debería dejarte con un objeto que esté en un estado válido. Si debe ejecutar un montón de comandos para restaurar el objeto a un estado válido, algo ha salido muy mal.
fuente
abstract
T getSelf()
método con retorna el tipo genérico. Ahora, en lugar de regresarthis
de sus establecedores, ustedreturn getSelf()
y cualquier clase anuladora simplemente hacen que el tipo sea genérico en sí mismo y regresathis
degetSelf
. De esta manera, los setters devuelven el tipo real y no el tipo de declaración.Guardar unas pocas teclas no es convincente. Puede ser agradable, pero las convenciones de OOP se preocupan más por conceptos y estructuras, no por pulsaciones de teclas.
El valor de retorno no tiene sentido.
Incluso más que no tener sentido, el valor de retorno es engañoso, ya que los usuarios pueden esperar que el valor de retorno tenga significado. Pueden esperar que sea un "setter inmutable"
En realidad tu setter muta el objeto.
No funciona bien con la herencia.
puedo escribir
pero no
Para mí, # 1 a # 3 son los más importantes. El código bien escrito no se trata de texto agradablemente organizado. El código bien escrito trata sobre los conceptos fundamentales, las relaciones y la estructura. El texto es solo un reflejo externo del verdadero significado del código.
fuente
public class Foo<T extends Foo> {...}
con el setter regresandoFoo<T>
. También esos métodos 'setter', prefiero llamarlos 'con' métodos. Si la sobrecarga funciona bien, a continuación, sóloFoo<T> with(Bar b) {...}
, de lo contrarioFoo<T> withBar(Bar b)
.No creo que esta sea una convención OOP, está más relacionada con el diseño del lenguaje y sus convenciones.
Parece que te gusta usar Java. Java tiene una especificación JavaBeans que especifica el tipo de retorno del setter a anular, es decir, está en conflicto con el encadenamiento de setters. Esta especificación es ampliamente aceptada e implementada en una variedad de herramientas.
Por supuesto, puede preguntar, ¿por qué no encadena parte de la especificación? No sé la respuesta, tal vez este patrón simplemente no era conocido / popular en ese momento.
fuente
Object
, lo que puede dar como resultado que se devuelva el singleton de tipoVoid
si el método se especificavoid
como su tipo de retorno. En otras noticias, aparentemente "dejar un comentario pero no votar" es motivo para reprobar una auditoría de "primera publicación", incluso si es un buen comentario. Culpo a Shog por esto.Como han dicho otras personas, esto a menudo se llama una interfaz fluida .
Normalmente los establecedores son llamadas que pasan variables en respuesta al código lógico en una aplicación; Su clase DTO es un ejemplo de esto. El código convencional cuando los setters no devuelven nada es lo mejor para esto. Otras respuestas han explicado de manera.
Sin embargo, hay algunos casos en los que la interfaz fluida puede ser una buena solución, estos tienen en común.
Configuración de la configuración, por ejemplo , nhibernate fluido
Configuración de datos de prueba en pruebas unitarias, usando TestDataBulderClasses (Object Mothers) especiales
Sin embargo, crear una buena interfaz fluida es muy difícil , por lo que solo vale la pena cuando tienes mucha configuración "estática". Además, la interfaz fluida no debe mezclarse con clases "normales"; por lo tanto, el patrón de construcción se usa a menudo.
fuente
Creo que gran parte de la razón por la que no es una convención encadenar un setter tras otro es porque para esos casos es más típico ver un objeto o parámetros de opciones en un constructor. C # también tiene una sintaxis de inicializador.
En lugar de:
Se podría escribir:
(en JS)
(Cía#)
(en Java)
setFoo
ysetBar
ya no son necesarios para la inicialización, y se pueden usar para la mutación más adelante.Si bien la capacidad de encadenamiento es útil en algunas circunstancias, es importante no intentar rellenar todo en una sola línea solo por reducir los caracteres de nueva línea.
Por ejemplo
hace que sea más difícil leer y comprender lo que está sucediendo. Reformateando a:
Es mucho más fácil de entender y hace que el "error" en la primera versión sea más obvio. Una vez que haya refactorizado el código a ese formato, no hay una ventaja real sobre:
fuente
/
para representar salto de línea]With Foo(1234) / .x = 23 / .y = 47
sería equivalente aDim temp=Foo(1234) / temp.x = 23 / temp.y = 47
. Dicha sintaxis no crea ambigüedad, ya que.x
por sí misma no puede tener otro significado que el de unirse a la declaración "Con" inmediatamente circundante [si no hay ninguno o si el objeto no tiene miembrox
, entonces no.x
tiene sentido]. Oracle no ha incluido algo así en Java, pero tal construcción encajaría sin problemas en el lenguaje.Esa técnica se usa en realidad en el patrón Builder.
Sin embargo, en general se evita porque es ambiguo. No es obvio si el valor de retorno es el objeto (por lo que puede llamar a otros establecedores), o si el objeto de retorno es el valor que se acaba de asignar (también un patrón común). En consecuencia, el Principio de Menos Sorpresa sugiere que no debe tratar de asumir que el usuario quiere ver una solución u otra, a menos que sea fundamental para el diseño del objeto.
fuente
Obj* x = doSomethingToObjAndReturnIt(new Obj(1, 2, 3));
que creo que también ganó popularidad porque reflejaa = b = c = d
, aunque no estoy convencido de que la popularidad esté bien fundada. He visto el que mencionas, devolviendo el valor anterior, en algunas bibliotecas de operaciones atómicas. ¿Moraleja de la historia? Es aún más confuso de lo que lo hice sonar =)new BetterText(string).indent(4).box().print();
. En ese caso, pasé por alto un montón de gobbledygook y tomé una cadena, la sangré, la recuadré y la saqué. Es posible que incluso desee un método de duplicación dentro de la cascada (como, por ejemplo,.copy()
) para permitir que todas las acciones siguientes ya no modifiquen el original.Esto es más un comentario que una respuesta, pero no puedo comentar, así que ...
Solo quería mencionar que esta pregunta me sorprendió porque no veo esto como algo poco común . En realidad, en mi entorno de trabajo (desarrollador web) es muy muy común.
Por ejemplo, así es como la doctrina de Symfony: genera: el comando de entidades genera automáticamente todo
setters
, de forma predeterminada .jQuery Kinda cadenas de la mayoría de sus métodos de una manera muy similar.
fuente
JAB
. En mis años de universidad solía mirar hacia abajo en JS como una abominación lenguaje de script aspirante a, pero después de trabajar un par de años con él, y el aprendizaje de la DERECHA forma de usarlo, me he dado ... en realidad el amor que . Y creo que con ES6 se convertirá en un lenguaje realmente maduro . Pero, lo que me hace girar la cabeza es ... ¿qué tiene que ver mi respuesta con JS en primer lugar? ^^ U