Ter o encadeamento implementado nos beans é muito útil: não é necessário sobrecarregar construtores, mega construtores, fábricas e oferece maior legibilidade. Não consigo pensar em nenhuma desvantagem, a menos que você queira que seu objeto seja imutável ; nesse caso, não haveria nenhum levantador de qualquer maneira. Então, existe uma razão para que essa não seja uma convenção 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();
Eu faria isso, para não entrar em conflito com a idéia geral de que levantadores são vazios.new Foo().setBar('bar').setBaz('baz')
parece muito "fluente". Quero dizer, com certeza ele poderia ser implementado exatamente da mesma maneira, mas eu esperaria muito ler algo mais parecido comFoo().barsThe('bar').withThe('baz').andQuuxes('the quux')
Respostas:
Meu melhor palpite: porque viola o CQS
Você tem um comando (alterando o estado do objeto) e uma consulta (retornando uma cópia do estado - nesse caso, o próprio objeto) misturados no mesmo método. Isso não é necessariamente um problema, mas viola algumas das diretrizes básicas.
Por exemplo, em C ++, std :: stack :: pop () é um comando que retorna nulo, e std :: stack :: top () é uma consulta que retorna uma referência ao elemento top na pilha. Classicamente, você gostaria de combinar os dois, mas não pode fazer isso e ter uma exceção segura. (Não é um problema em Java, porque o operador de atribuição em Java não lança).
Se o DTO fosse um tipo de valor, você poderia obter um final semelhante com
Além disso, os valores de retorno do encadeamento são uma dor colossal quando você está lidando com herança. Consulte o "Padrão de modelo curiosamente recorrente"
Finalmente, há o problema de que o construtor padrão deve deixá-lo com um objeto que esteja em um estado válido. Se você precisar executar vários comandos para restaurar o objeto para um estado válido, algo deu muito errado.
fonte
abstract
T getSelf()
método com retorna o tipo genérico. Agora, em vez de retornarthis
de seus setters, vocêreturn getSelf()
e qualquer classe substituindo simplesmente faz o tipo genérico em si e voltathis
degetSelf
. Dessa forma, os setters retornam o tipo real e não o tipo declarante.Salvar algumas teclas não é convincente. Pode ser bom, mas as convenções de OOP se preocupam mais com conceitos e estruturas, não com pressionamentos de teclas.
O valor de retorno não tem sentido.
Mais do que não ter sentido, o valor de retorno é enganador, pois os usuários podem esperar que o valor de retorno tenha significado. Eles podem esperar que seja um "levantador imutável"
Na realidade, o seu setter muda o objeto.
Não funciona bem com herança.
eu consigo escrever
mas não
Para mim, os números 1 a 3 são os mais importantes. Código bem escrito não é sobre texto agradavelmente organizado. Um código bem escrito trata dos conceitos, relacionamentos e estrutura fundamentais. O texto é apenas um reflexo externo do verdadeiro significado do código.
fonte
public class Foo<T extends Foo> {...}
com o setter retornandoFoo<T>
. Também esses métodos 'setter', prefiro chamá-los de 'com' métodos. Se a sobrecarga funcionar bem, apenas oFoo<T> with(Bar b) {...}
contrárioFoo<T> withBar(Bar b)
.Eu não acho que essa seja uma convenção OOP, está mais relacionada ao design da linguagem e suas convenções.
Parece que você gosta de usar Java. Java possui uma especificação JavaBeans que especifica o tipo de retorno do configurador a ser anulado, ou seja, está em conflito com o encadeamento de configuradores. Esta especificação é amplamente aceita e implementada em uma variedade de ferramentas.
Claro que você pode perguntar, por que não está encadeando parte da especificação. Não sei a resposta, talvez esse padrão não fosse conhecido / popular na época.
fonte
Object
, o que pode resultar no retorno do singleton do tipoVoid
se o método especificarvoid
como seu tipo de retorno. Em outras notícias, aparentemente "deixar um comentário, mas não votar" é motivo para falhar na auditoria do "primeiro post", mesmo que seja um bom comentário. Eu culpo o shog por isso.Como outras pessoas disseram, isso geralmente é chamado de interface fluente .
Normalmente, os setters são variáveis de passagem de chamadas em resposta ao código lógico em um aplicativo; sua classe de DTO é um exemplo disso. O código convencional quando os setters não retornam nada é o melhor para isso. Outras respostas explicaram o caminho.
No entanto, existem alguns casos em que a interface fluente pode ser uma boa solução, estes têm em comum.
Definindo a configuração, por exemplo, fluent-nhibernate
Configurando dados de teste em testes de unidade, usando TestDataBulderClasses especial (mães de objeto)
No entanto, a criação de uma interface fluente boa é muito difícil , por isso só vale a pena quando você tem muita configuração "estática". Também a interface fluente não deve ser misturada às classes "normais"; portanto, o padrão do construtor é frequentemente usado.
fonte
Eu acho que muito do motivo de não ser uma convenção encadear um setter após o outro é porque, nesses casos, é mais comum ver um objeto de opções ou parâmetros em um construtor. O C # também possui uma sintaxe de inicializador.
Ao invés de:
Alguém pode escrever:
(em JS)
(em c #)
(em Java)
setFoo
e nãosetBar
são mais necessários para a inicialização e podem ser usados para mutação posteriormente.Embora a capacidade de encadeamento seja útil em algumas circunstâncias, é importante não tentar colocar tudo em uma única linha apenas para reduzir os caracteres de nova linha.
Por exemplo
torna mais difícil ler e entender o que está acontecendo. Reformatação para:
É muito mais fácil de entender e torna o "erro" na primeira versão mais óbvio. Depois de refatorar o código para esse formato, não há vantagem real sobre:
fonte
/
to represente quebra de linha]With Foo(1234) / .x = 23 / .y = 47
seja equivalente aDim temp=Foo(1234) / temp.x = 23 / temp.y = 47
. Essa sintaxe não cria ambiguidade, pois,.x
por si só, não pode ter outro significado senão vincular-se à declaração "With" imediatamente circundante [se não houver, ou se o objeto não tiver membrox
, então não terá.x
sentido]. A Oracle não incluiu nada disso em Java, mas essa construção se encaixaria perfeitamente na linguagem.Essa técnica é realmente usada no padrão Builder.
No entanto, em geral, é evitado porque é ambíguo. Não é óbvio se o valor de retorno é o objeto (para que você possa chamar outros setters) ou se o objeto de retorno é o valor que acabou de ser atribuído (também um padrão comum). Assim, o Princípio da menor surpresa sugere que você não deve tentar assumir que o usuário deseja ver uma solução ou outra, a menos que seja fundamental para o design do objeto.
fonte
Obj* x = doSomethingToObjAndReturnIt(new Obj(1, 2, 3));
que acho que também ganhou popularidade porque refletea = b = c = d
, embora não esteja convencido de que a popularidade tenha sido bem fundamentada. Eu vi o que você mencionou, retornando o valor anterior, em algumas bibliotecas de operações atômicas. Moral da história? É ainda mais confuso do que eu fez soar =)new BetterText(string).indent(4).box().print();
. Nesse caso, eu ignorei um monte de bobagens e peguei uma corda, recuei, encaixotei e saí. Você pode até querer um método de duplicação dentro da cascata (como, por exemplo,.copy()
) para permitir que todas as ações a seguir não modifiquem mais o original.Isso é mais um comentário do que uma resposta, mas não posso comentar, então ...
só queria mencionar que essa pergunta me surpreendeu porque não vejo isso de maneira incomum . Na verdade, no meu ambiente de trabalho (desenvolvedor web) é muito, muito comum.
Por exemplo, é assim que o comando doutrina: generate: entity do Symfony gera automaticamente tudo
setters
, por padrão .O jQuery meio que encadeia a maioria de seus métodos de uma maneira muito semelhante.
fonte
JAB
. Nos meus anos de faculdade, eu costumava olhar para JS como uma linguagem de script de abominação, mas depois de trabalhar alguns anos com ela e aprender a maneira correta de usá-la, cheguei a ... realmente adorá- la . E acho que com o ES6 se tornará uma linguagem realmente madura . Mas, o que me faz virar a cabeça é ... o que minha resposta tem a ver com JS em primeiro lugar? ^^ U