É uma prática ruim fazer um levantador retornar "isso"?

249

É uma boa ou má idéia fazer com que os setters em java retornem "isso"?

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

Esse padrão pode ser útil porque você pode encadear setters como este:

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

em vez disso:

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

... mas isso vai contra a convenção padrão. Suponho que possa valer a pena apenas porque pode fazer com que o levantador faça outra coisa útil. Eu já vi esse padrão usado em alguns lugares (por exemplo, JMock, JPA), mas parece incomum e geralmente usado apenas para APIs muito bem definidas, onde esse padrão é usado em qualquer lugar.

Atualizar:

O que eu descrevi é obviamente válido, mas o que realmente estou procurando é pensar se isso geralmente é aceitável e se existem armadilhas ou práticas recomendadas relacionadas. Eu sei sobre o padrão Builder, mas ele é um pouco mais envolvido do que o que estou descrevendo - como Josh Bloch descreve, há uma classe estática associada do Builder para criação de objetos.

Ken Liu
fonte
1
Desde que eu vi esse padrão de design há algum tempo, faço isso sempre que possível. Se um método não precisar explicitamente retornar algo para fazer seu trabalho, ele retornará agora this. Às vezes, até altero a função para que, em vez de retornar um valor, ela opere em um membro do objeto, apenas para que eu possa fazer isso. É maravilhoso. :)
Inversus
4
Para setters telescópicos retornando self e em construtores, prefiro usar withName (String name) em vez de setName (String name). Como você apontou, uma prática e expectativa comuns para o levantador é retornar vazio.
Setters
Por favor, introduza quebras de linha antes de cada chamada :) E configure seu IDE ou obtenha um adequado se não respeitar isso.
MauganRa 23/02
Estruturas amplamente utilizado (Spring e Hibernate, por exemplo) será estritamente (pelo menos eles costumavam) aderir de anular-setters convenção
Legna

Respostas:

83

Não acho que exista algo especificamente errado, é apenas uma questão de estilo. É útil quando:

  • Você precisa definir vários campos ao mesmo tempo (inclusive na construção)
  • você sabe quais campos precisa definir no momento em que está escrevendo o código e
  • existem muitas combinações diferentes para quais campos você deseja definir.

Alternativas para esse método podem ser:

  1. Um mega construtor (desvantagem: você pode passar muitos valores nulos ou padrão, e fica difícil saber qual valor corresponde a qual)
  2. Vários construtores sobrecarregados (desvantagem: fica difícil quando você tem mais do que alguns)
  3. Métodos de fábrica / estáticos (desvantagem: o mesmo que os construtores sobrecarregados - fica difícil quando há mais do que alguns)

Se você apenas definir algumas propriedades por vez, diria que não vale a pena retornar 'this'. Certamente diminui se você decidir retornar outra coisa, como um indicador de status / sucesso / mensagem.

Tom Clift
fonte
1
bem, geralmente você não retorna nada de um levantador de qualquer maneira, por convenção.
28511 Ken Liu
17
Talvez não seja para começar, mas um levantador não mantém necessariamente seu propósito original. O que costumava ser uma variável pode mudar para um estado que engloba várias variáveis ​​ou tem outros efeitos colaterais. Alguns setters podem retornar um valor anterior, outros podem retornar um indicador de falha se a falha for muito comum para uma exceção. Isso levanta outro ponto interessante: e se uma ferramenta / estrutura que você estiver usando não reconhecer seus setters quando eles tiverem valores retornados?
Tom Clift
12
@ Tom bom ponto, fazer isso quebra a convenção "Java bean" para getters e setters.
Andy White
2
@TomClift A quebra da convenção "Java Bean" causa problemas? As bibliotecas que usam a convenção "Java Bean" examinam o tipo de retorno ou apenas os parâmetros e o nome do método.
Theo Briscoe
3
é por isso que existe o padrão Builder, setters não deve retornar algo, em vez disso, criar um construtor se a sua aparência necessária uma melhor e leva menos de código :)
RicardoE
106

Não é uma prática ruim. É uma prática cada vez mais comum. A maioria dos idiomas não exige que você lide com o objeto retornado, se não desejar, para que ele não mude a sintaxe de uso "normal" do setter, mas permita que você encadeie os setters.

Isso geralmente é chamado de padrão de construtor ou interface fluente .

Também é comum na API Java:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();
cleto
fonte
26
É frequentemente usado em construtores, mas eu não diria "isso é ... chamado de padrão Construtor".
Laurence Gonsalves
10
É engraçado para mim que parte da justificativa para interfaces fluentes é que elas facilitam a leitura do código. Pude ver que era mais conveniente escrever, mas me parece mais difícil de ler. Esse é o único desacordo real que tenho com isso.
Brent escreve código
30
Também é conhecido como antipadrão de trem-naufrágio. O problema é que, quando um rastreamento de pilha de exceção de ponteiro nulo contém uma linha como esta, você não tem idéia de qual chamada retornou nulo. Isso não quer dizer que o encadeamento deva ser evitado a todo custo, mas cuidado com as bibliotecas ruins (especialmente a home-brewn).
precisa saber é o seguinte
18
@ddimitrov levamos como você limitá-lo a retornar esse nunca seria um problema (apenas a primeira invocação poderia jogar NPE)
Stefan
4
é mais fácil escrever E ler, assumindo que você coloque quebras de linha e indentação onde a legibilidade seria prejudicada! (porque evita a confusão redundante de código repetido como bla.foo.setBar1(...) ; bla.foo.setBar2(...)quando você pode escrever bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(não é possível usar quebras de linha em um comentário do SO como este :-( ... espero que você entenda isso considerando dez desses setters ou chamadas mais complexas)
Andreas Dietrich
90

Para resumir:

  • é chamado de "interface fluente" ou "encadeamento de métodos".
  • isso não é Java "padrão", embora você o veja mais hoje em dia (funciona muito bem no jQuery)
  • viola a especificação do JavaBean e, portanto, rompe com várias ferramentas e bibliotecas, especialmente os construtores JSP e Spring.
  • isso pode impedir algumas otimizações que a JVM normalmente faria
  • algumas pessoas acham que ele limpa o código, outras acham "horrível"

Alguns outros pontos não mencionados:

  • Isso viola o princípio de que cada função deve fazer uma (e apenas uma) coisa. Você pode ou não acreditar nisso, mas em Java eu ​​acredito que funciona bem.

  • Os IDEs não vão gerá-los para você (por padrão).

  • Finalmente, aqui está um ponto de dados do mundo real. Eu tive problemas ao usar uma biblioteca criada assim. O construtor de consultas do Hibernate é um exemplo disso em uma biblioteca existente. Como os métodos set * da consulta estão retornando consultas, é impossível saber apenas olhando a assinatura como usá-la. Por exemplo:

    Query setWhatever(String what);
  • Isso introduz uma ambiguidade: o método modifica o objeto atual (seu padrão) ou, talvez, a consulta seja realmente imutável (um padrão muito popular e valioso) e o método está retornando um novo. Isso apenas torna a biblioteca mais difícil de usar e muitos programadores não exploram esse recurso. Se os setters fossem setters, seria mais claro como usá-lo.

ndp
fonte
5
btw, é "fluente", não "fluido" ... pois permite estruturar uma série de chamadas de método como um idioma falado.
22815 Ken Liu
1
O último ponto sobre imutabilidade é muito importante. O exemplo mais simples é String. Os desenvolvedores Java esperam que, ao usar métodos em String, eles obtenham uma instância totalmente nova e não a mesma, mas modificada. Com uma interface fluente, seria necessário mencionar na documentação do método que o objeto retornado é 'this' em vez de nova instância.
MeTTeO 19/09/2015
7
Embora eu concorde de maneira geral, discordo que isso viola o princípio "faça apenas uma coisa". Voltar thisdificilmente é ciência complexa de foguetes. :-)
user949300 30/10
ponto adicional: também viola o princípio de separação Comando-consulta.
28718 Marton_hun
84

Eu prefiro usar métodos 'with' para isso:

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

Portanto:

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

Aviso : essa withXsintaxe é comumente usada para fornecer "setters" para objetos imutáveis; portanto, os chamadores desses métodos podem esperar razoavelmente que eles criem novos objetos em vez de mutarem a instância existente. Talvez uma redação mais razoável seja algo como:

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

Com a convenção de nomenclatura chainsetXyz (), praticamente todos devem estar felizes.

qualidafial
fonte
18
+1 Para convenções interessantes. Não vou adotá-lo no meu próprio código, pois parece que agora você precisa ter um get, um sete um withpara cada campo de classe. Ainda é uma solução interessante, no entanto. :)
Paul Manta
1
Depende da frequência com que você liga para os levantadores. Descobri que, se esses setters são chamados muito, vale a pena o trabalho extra de adicioná-los, pois simplifica o código em qualquer outro lugar. YMMV
qualidafial 24/10/11
2
E se você adicionou isto Project Lombok's @Getter/@Setteranotações ... isso seria fantástico para o encadeamento. Ou você pode usar algo parecido com o Kestrelcombinador ( github.com/raganwald/Katy ) que os viciados em JQuery e Javascript usam.
Ehtesh Choudhury
4
@ AlikElzin-kilaka Na verdade, acabei de notar que as classes imutáveis ​​java.time no Java 8 usam esse padrão, por exemplo, LocalDate.withMonth, withYear, etc.
qualidafial
4
o withprefixo é uma convenção diferente. como @qualidafial deu um exemplo. Os métodos prefixados com withnão devem retornar, thismas uma nova instância como a instância atual, mas withque muda. Isso é feito quando você deseja que seus objetos sejam imutáveis. Então, quando vejo um método prefixado with, presumo que receberei um novo objeto, não o mesmo objeto.
tempcke
26

Se você não deseja retornar 'this'do configurador, mas não deseja usar a segunda opção, pode usar a seguinte sintaxe para definir propriedades:

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

Como um aparte, acho que é um pouco mais limpo em C #:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});
Luke Quinane
fonte
16
a inicialização entre chaves pode ter problemas com iguais porque cria uma classe interna anônima; consulte c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H 28/08/09
@Csaba_H Claramente, esse problema é culpa da pessoa que confundiu o equalsmétodo. Existem maneiras muito limpas de lidar com classes anônimas, equalsse você souber o que está fazendo.
precisa saber é o seguinte
criando uma nova classe (anônima) apenas para isso? para cada instância?
user85421
11

Ele não apenas quebra a convenção de getters / setters, como também quebra a estrutura de referência do método Java 8. MyClass::setMyValueé um BiConsumer<MyClass,MyValue>e myInstance::setMyValueé um Consumer<MyValue>. Se você retornar seu setter this, ele não será mais uma instância válida Consumer<MyValue>, mas sim um Function<MyValue,MyClass>, e fará com que qualquer coisa que use referências de método a esses setters (supondo que sejam métodos nulos) seja interrompida.

Steve K
fonte
2
Seria incrível se o Java tivesse alguma maneira de sobrecarregar por tipo de retorno, não apenas pela JVM. Você pode ignorar muitas dessas alterações facilmente.
Adowrath 29/03
1
Você sempre pode definir uma interface funcional que estenda ambos Consumer<A>e Function<A,B>fornecendo uma implementação padrão de void accept(A a) { apply(a); }. Em seguida, ele pode ser facilmente usado como um deles e não quebrará nenhum código que exija um formulário específico.
21720 Steve
Argh! Isso é simplesmente errado! Isso é chamado de compatibilidade com o vazio. Um levantador que retorna um valor pode atuar como consumidor. ideone.com/ZIDy2M
Michael
8

Eu não sei Java, mas eu fiz isso em C ++. Outras pessoas disseram que torna as linhas muito longas e muito difíceis de ler, mas eu já fiz isso assim muitas vezes:

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

Isto é ainda melhor:

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

pelo menos eu acho. Mas não hesite em me votar e me chamar de péssimo programador, se desejar. E não sei se você pode fazer isso em Java.

Carson Myers
fonte
O "ainda melhor" não é bom para a funcionalidade de código-fonte Format disponível nos IDE modernos. Infelizmente.
Thorbjørn Ravn Andersen
você provavelmente está certo ... O único formatador automático que usei é o recuo automático do emacs.
Carson Myers
2
Os formatadores de código-fonte podem ser coagidos com um // simples depois de cada chamada de método na cadeia. Isso prejudica um pouco o seu código, mas não muito, pois sua série vertical de instruções é reformatada horizontalmente.
Qualidafial
@qualidafial Você não precisa, //após cada método, configurar o IDE para não unir linhas já quebradas (por exemplo, Eclipse> Propriedades> Java> Estilo de Código> Formatador> Quebra de Linha> Nunca unir linhas já quebradas).
DJDaveMark 16/01/19
6

Este esquema (trocadilhos), chamado de 'interface fluente', está se tornando bastante popular agora. É aceitável, mas não é realmente minha xícara de chá.

Seda do meio-dia
fonte
6

Como ele não retorna nulo, não é mais um configurador de propriedade JavaBean válido. Isso pode importar se você é uma das sete pessoas no mundo usando ferramentas visuais "Bean Builder" ou uma das 17 usando elementos JSP-bean-setProperty.

Ken
fonte
Também é importante se você usa estruturas compatíveis com bean como o Spring.
precisa saber é o seguinte
6

Pelo menos em teoria , isso pode danificar os mecanismos de otimização da JVM, definindo dependências falsas entre chamadas.

Supõe-se que seja açúcar sintático, mas de fato pode criar efeitos colaterais na máquina virtual do Java 43 super-inteligente.

É por isso que eu voto não, não use.

Marian
fonte
10
Interessante ... você poderia expandir um pouco isso?
28511 Ken Liu
3
Pense em como os processadores superescalares lidam com a execução paralela. O objeto para executar o segundo setmétodo depende do primeiro setmétodo, embora seja conhecido pelo programador.
Marian
2
Eu ainda não sigo. Se você definir Foo e depois Bar com duas instruções separadas, o objeto para o qual você está configurando Bar terá um estado diferente do objeto para o qual você está configurando Foo. Portanto, o compilador também não pôde paralelizar essas instruções. Pelo menos, não vejo como isso poderia ser feito sem a introdução de uma suposição injustificada. (Como não tenho idéia sobre isso, não negarei que o Java 43 realmente faça a paralelização no caso anterior, mas não no outro, e introduza a suposição injustificada em um caso, mas não no outro).
masonk
12
Se você não sabe, teste. -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining O java7 jdk definitivamente alinha métodos encadeados e realiza o mesmo número de iterações necessárias para marcar os setters de vácuo como quentes e incorporá-los também. Parece que você subestima o poder dos algoritmos de remoção de código de operação da JVM; se souber que você está retornando isso, pulará o código de operação jrs (declaração de retorno do java) e o deixará na pilha.
Ajax
1
Andreas, eu concordo, mas os problemas aparecem quando você tem camadas e mais camadas de código ineficiente. Em 99% do tempo, você deve codificar com clareza, o que é dito muito por aqui. Mas também há momentos em que você precisa ser realista e usar seus anos de experiência para otimizar prematuramente, no sentido arquitetônico geral.
LegendLength
6

Não é uma prática ruim. Mas não é compatível com o JavaBeans Spec .

E há muita especificação depende desses acessadores padrão.

Você sempre pode fazê-los coexistir um com o outro.

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

Agora podemos usá-los juntos.

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

Aí vem outra versão para objeto imutável.

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

Agora podemos fazer isso.

new Some.Builder().value("value").build().getValue();
Jin Kwon
fonte
2
Minha edição foi rejeitada, mas seu exemplo do Builder não está correto. Primeiro, .value () não retorna nada e nem define o somecampo. Em segundo lugar, você deve adicionar uma salvaguarda e conjunto somepara nullno build () assim Someé verdadeiramente imutável, caso contrário você pode chamar builder.value()na mesma instância Builder novamente. E, finalmente, sim, você tem um construtor, mas Someainda possui um construtor público, o que significa que você não defende abertamente o uso do construtor, ou seja, o usuário não o conhece senão tentando ou procurando um método para definir um costume. valueem absoluto.
Adowrath 30/03/19
@Adowrath se a resposta for incorreta, você deve escrever sua própria resposta, não tentar e editar de outra pessoa em forma
CalvT
1
@JinKwon Great. Obrigado! E desculpe se eu parecia rude antes.
Adowrath 18/08
1
@Adowrath Por favor, sinta-se à vontade para quaisquer comentários adicionais sobre melhorias. Para sua informação, não fui eu quem rejeitou sua edição. :)
Jin Kwon
1
Eu sei eu sei. ^^ E obrigado pela nova versão, que agora fornece um construtor mutável "Imutável-Alguns". E é uma solução mais inteligente do que minhas tentativas de edição que, em comparação, atrapalharam o código.
Adowrath 18/08
4

Se você usar a mesma convenção em todo o aplicativo, tudo bem.

Por outro lado, se parte existente do seu aplicativo usa a convenção padrão, eu o manteria e adicionaria construtores a classes mais 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
fonte
1
Isso é exatamente o que o padrão Builder foi projetado para corrigir. Ele não quebra as lambdas no Java 8, não quebra as ferramentas sofisticadas do JavaBeans e não causa problemas de otimização na JVM (já que o objeto existe apenas durante a instanciação). Ele também resolve o problema do "número excessivo de construtores" que você obtém ao simplesmente não usar construtores, além de eliminar a poluição de pilha das classes anônimas entre chaves.
Ndm13
Ponto interessante - estou percebendo que, se quase nada em sua classe for imutável (por exemplo, uma GUI altamente configurável), você provavelmente poderá evitar o Builder por completo.
Philip Guin
4

Paulo Abrantes oferece outra maneira de tornar os setters JavaBean fluentes: defina uma classe de construtor interno para cada JavaBean. Se você estiver usando ferramentas que ficam confusas com setters que retornam valores, o padrão de Paulo pode ajudar.

Jim Ferrans
fonte
2
@ cdunn2001, use
msangel
3

Sou a favor de setters terem retornos "this". Eu não me importo se não é compatível com feijão. Para mim, se não há problema em ter a expressão / instrução "=", então os configuradores que retornam valores estão corretos.

Monty Hall
fonte
2

Eu preferia essa abordagem, mas decidi contra.

Razões:

  • Legibilidade. Torna o código mais legível para cada setFoo () em uma linha separada. Você geralmente lê o código muitas, muitas vezes mais do que a única vez em que o escreve.
  • Efeito colateral: setFoo () deve apenas definir o campo foo, nada mais. Retornando isso é um extra "O que foi isso".

O padrão do Builder que vi não usa a convenção setFoo (foo) .setBar (bar), mas mais foo (foo) .bar (bar). Talvez por exatamente essas razões.

É, como sempre, uma questão de gosto. Eu apenas gosto da abordagem "menos surpresas".

Thorbjørn Ravn Andersen
fonte
2
Eu concordo com o efeito colateral. Os setters que retornam itens violam seu nome. Você está definindo foo, mas você recebe um objeto de volta? Este é um novo objeto ou alterei o antigo?
crunchdog
2

Esse padrão específico é chamado de encadeamento de métodos. Link da Wikipedia , isso tem mais explicações e exemplos de como é feito em várias linguagens de programação.

PS: Apenas pensei em deixá-lo aqui, já que eu estava procurando pelo nome específico.

capt.swag
fonte
1

À primeira vista: "Medonho!".

Pensando melhor

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

é realmente menos propenso a erros do que

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

Tão interessante. Adicionando ideia à maleta de ferramentas ...

djna
fonte
1
Não, ainda é horrível. Do ponto de vista da manutenção, o último é melhor porque é mais fácil de ler. Além disso, verificadores de código automatizados como o CheckStyle imporão linhas com 80 caracteres por padrão - o código será quebrado de qualquer maneira, compondo o problema de legibilidade / manutenção. E finalmente - é Java; não há nenhum benefício em escrever tudo em uma única linha quando ele for compilado para código de bytes.
OMG Ponies
1
pessoalmente, acho que o primeiro é mais fácil de ler, especialmente se você estiver criando vários objetos dessa maneira.
28511 Ken Liu
@ Ken: Código de um método. Escreva uma cópia em formato fluente; outra cópia na outra. Agora, entregue as duas cópias a algumas pessoas e pergunte qual delas elas acham mais fácil de ler. Mais rápido para ler, mais rápido para codificar.
OMG Ponies
Como a maioria das ferramentas, pode ser fácil usar demais. O JQuery é orientado em torno dessa técnica e, portanto, propenso a longas cadeias de chamadas que eu descobri que na verdade prejudicam a legibilidade.
staticsan
2
Tudo bem se houvesse quebras de linha antes de cada ponto e recuar. Então, seria tão legível quanto a segunda versão. Na verdade, mais, porque não há redundância listno início.
MauganRa 23/02
1

Sim, acho que é uma boa ideia.

Se eu pudesse adicionar algo, e esse 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;
    }
}

Isso funcionará:

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

Isso não será aceito pelo Eclipse! :

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

Isso ocorre porque setName () retorna um povo e não um amigo, e não há PeoplesetNickName.

Como poderíamos escrever setters para retornar a classe SELF em vez do nome da classe?

Algo assim seria bom (se a palavra-chave SELF existir). Existe mesmo assim?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}
Baptiste
fonte
1
Existem alguns outros compiladores Java além do Eclipse que não aceitam isso :). Basicamente, você está executando o sistema de tipos Java (que é estático, não dinâmico como algumas linguagens de script): você terá que converter o que sai de setName () para um amigo antes de poder definirNickName () nele. Portanto, infelizmente, para as hierarquias de herança, isso elimina grande parte da vantagem de legibilidade e torna essa técnica potencialmente útil não tão útil.
Cornel Masson
5
Use genéricos. classe Chainable <Self extends Chainable> {public Self doSomething () {return (Self) this;}} Não é tecnicamente seguro quanto ao tipo (ele classificará a conversão se você implementar a classe com um tipo Self do qual você não pode ser); é a gramática correta e as subclasses retornam seu próprio tipo.
Ajax
Além disso: esse "EU" que Baptiste está usando é chamado de tipo próprio, não presente em muitas línguas (Scala é realmente o único em que posso pensar agora), onde o uso de genéricos do Ajax é o chamado "Curiosamente modelo padrão", que tenta lidar com a falta do tipo auto recorrentes, mas tem alguns inconvenientes: (a partir class C<S extends C<S>>, o que é mais seguro do que uma planície S extends C. a) Se A extends B, e B extends C<B>, e você tem um a, você só sabe que um B é retornado, a menos que A substitua cada método. b) Você não pode indicar um local, não bruto C<C<C<C<C<...>>>>>.
Adowrath
1

Em geral, é uma boa prática, mas pode ser necessário que as funções do tipo set usem o tipo booleano para determinar se a operação foi concluída com êxito ou não, essa também é uma maneira. Em geral, não há dogma para dizer que isso é bom ou de cama, vem da situação, é claro.

Narek
fonte
2
Que tal usar exceções para indicar condição de erro? Os códigos de erro podem ser facilmente ignorados, como muitos programadores C aprenderam dolorosamente. Exceções podem formar bolhas na pilha até o ponto em que podem ser manipuladas.
precisa saber é o seguinte
Geralmente, as exceções são preferíveis, mas os códigos de erro também são úteis quando você não pode usar exceções.
Narek
1
Olá @Narek, talvez você possa explicar em que casos é preferível usar códigos de erro em setters, em vez de exceções e por quê?
Ddimitrov 16/01/19
0

Da declaração

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

eu estou vendo duas coisas

1) Declaração sem sentido. 2) Falta de legibilidade.

Madhu
fonte
0

Isso pode ser menos legível

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

ou isto

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

Isso é muito mais legível do que:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 
Scott
fonte
8
Eu acho que é bem legível se você não tentar colocar todo o seu código em uma linha.
Ken Liu
0

Faço meus setters há um bom tempo e o único problema real é com bibliotecas que atendem aos estritos getPropertyDescriptors para obter os acessadores de bean / escritor do bean. Nesses casos, seu "bean" java não terá os gravadores que você esperaria.

Por exemplo, eu não testei com certeza, mas não ficaria surpreso que Jackson não os reconheça como setters ao criar objetos java a partir do json / maps. Espero estar errado neste (testarei em breve).

De fato, estou desenvolvendo um ORM centralizado em SQL leve e preciso adicionar algum código além de getPropertyDescriptors a setters reconhecidos que retornem isso.

Jeremy Chone
fonte
0

Resposta há muito tempo, mas meus dois centavos ... Está tudo bem. Eu gostaria que essa interface fluente fosse usada com mais frequência.

Repetir a variável 'factory' não adiciona mais informações abaixo:

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

Isso é mais limpo, imho:

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

Obviamente, como uma das respostas já mencionadas, a API Java teria que ser ajustada para fazer isso corretamente em algumas situações, como herança e ferramentas.

Josef.B
fonte
0

É melhor usar outras construções de idioma, se disponíveis. Por exemplo, em Kotlin, você usaria com , aplicar , ou deixe . Se você usar essa abordagem, não precisará realmente retornar uma instância do seu setter.

Essa abordagem permite que seu código de cliente seja:

  • Indiferente ao tipo de retorno
  • Mais fácil de manter
  • Evite efeitos colaterais do compilador

Aqui estão alguns exemplos.

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
fonte
0

Se estou escrevendo uma API, uso "return this" para definir valores que serão definidos apenas uma vez. Se eu tiver outros valores que o usuário possa alterar, use um configurador de nulo padrão.

No entanto, é realmente uma questão de preferência e, em minha opinião, os setters de encadeamento parecem bem legais.

Kaiser Keister
fonte
0

Eu concordo com todos os pôsteres afirmando que isso quebra as especificações do JavaBeans. Há razões para preservar isso, mas também sinto que o uso desse Padrão do Construtor (que foi mencionado) tem seu lugar; contanto que não seja usado em todos os lugares, deve ser aceitável. "It's Place", para mim, é onde o ponto final é uma chamada para um método "build ()".

Existem outras maneiras de definir todas essas coisas, é claro, mas a vantagem aqui é que ela evita 1) construtores públicos de muitos parâmetros e 2) objetos parcialmente especificados. Aqui, você faz o construtor coletar o que é necessário e depois chamar "build ()" no final, o que pode garantir que um objeto parcialmente especificado não seja construído, já que essa operação pode ter menos visibilidade que o público. A alternativa seria "objetos de parâmetro", mas esse IMHO apenas empurra o problema de volta a um nível.

Não gosto de construtores de muitos parâmetros porque eles tornam mais provável a passagem de muitos argumentos do mesmo tipo, o que pode facilitar a transmissão de argumentos errados para parâmetros. Não gosto de usar muitos setters porque o objeto pode ser usado antes de ser totalmente configurado. Além disso, a noção de ter valores padrão baseados em escolhas anteriores é melhor servida com o método "build ()".

Em suma, acho que é uma boa prática, se usada corretamente.

Javaneer
fonte
-4

Mau hábito: um setter define um getter

que tal declarar explicitamente um método, que faz isso por U

setPropertyFromParams(array $hashParamList) { ... }
Ulrich Tevi Horus
fonte
não é bom para auto-preenchimento e refatoração e legibilidade do código ANB clichê
Andreas Dietrich
Porque: 1) É necessário lembrar a ordem ou lê-la nos documentos, 2) você perde toda a segurança do tipo em tempo de compilação, 3) precisa converter os valores (this e 2) é, obviamente, um problema na dinâmica linguagem é claro), 4) você pode não estender esta outra forma útil de verificar a matriz de comprimento em cada chamada, e 5) se você mudar alguma coisa, seja ele pedir ou mesmo a existência de certos valores, você pode não verificar que nem tempo de execução nem compilar timewithout dúvida em tudo . Com setters: 1), 2), 3): Não há problema. 4) Adicionar novos métodos não quebra nada. 5) mensagem de erro explícita.
Adowrath 29/03