Costruttori automatici / chain setter per Java?

7

Considera quel codice:

FancyClass c = new FancyClass();
s.setParameter(value);
s.setParameter2(value2);
//a lot of parameters

È davvero ingenuo, quindi possiamo usare un modello di builder:

FancyClass c = new FancyClassBuilder().setParameter(value).setParameter2(value2).build()

Va bene, ma cosa succede se dobbiamo usare un sacco di setter durante un programma?

s.setParameter(value)
s.setParameter1(value)
s.setParameter2(value)
s.setParameter3(value)
//more and more...

Sarà più efficiente e meno caldaia:

s.setParameter(value).setParameter1(value).setParameter2(value)
 .setParameter3(value)

Sembra più a posto e consente al programmatore di non eseguire questa azione: (lo odio):

s.setParamter()
// a lot of doing smth
s.setParameter2()

anche se non è necessario mettere via questi setter (perché si tratta di un problema ereditario, qualcuno semplicemente tocca l'invio due volte e qualcuno digita un altro codice ...

Che cosa succede se ...

@SetterChain
class MyFancyClass {
  public void setParameter(Parameter p) {}
  public Parameter getParameter(Parameter p) {}
  public void setParameter2(Parameter p) {}
  public Parameter getParameter2(Parameter p) {}
}

//somewhere else...

MyFancyClass s = new MyFancyClass()

//a long time ago

s.set().parameter(value).parameter2(value); //and more and more...

Cose interessanti o no? Questa soluzione aumenterebbe significativamente la qualità di un codice o meno e si tratta di un relitto e perché?

    
posta Dawid Pura 24.04.2015 - 15:37
fonte

4 risposte

3

Il pattern Builder risolve un problema particolare che si presenta - quei costruttori con una dozzina (o più!) argomenti. Sì, c'è spesso un altro problema in agguato con tali oggetti, ma il Builder fa in modo che uno non abbia bisogno di troppi costruttori per gestire tutte le diverse varianti su come l'oggetto può essere costruito.

Un setter di stile fluente (dove il setter restituisce this ), non ha questo problema. A parte alcuni fastidi stilistici, non vedo un problema che deve essere risolto.

Avere un'annotazione in fase di compilazione, mentre è pulito, interessante e potente aggiunge una notevole quantità di complessità alla costruzione del progetto e rende difficile per qualcuno (o qualcosa) leggere il codice per ragionarlo. Certo, puoi annotarlo e fare in modo che il compilatore faccia la sua magia ... ma spesso c'è qualcosa che precede il compilatore (a parte il programmatore) che deve occuparsi del codice.

Il "qualcosa" che legge il codice è una vera preoccupazione qui. L'IDE non conosce il metodo set() . Né sa cosa restituisce, né tutti i metodi che ha . Ciò renderebbe la vista dell'IDE della classe una massa di marcatori rossi "questo non esiste".

L'approccio set() è anche lontano dallo standard (e in realtà non risolve un problema reale) e rende più difficile per un nuovo coder arrivare al codice base e capirlo. Aggiunge complessità non necessaria al processo di compilazione e può rendere l'IDE infelice.

Anche se tutto ciò può essere bello e intelligente (sì, penso che le annotazioni siano interessanti e intelligenti e abbiano una relazione di amore / odio con loro), non penso che questa sia una buona strada da seguire.

    
risposta data 24.04.2015 - 16:30
fonte
5

Sebbene non sia esattamente uguale a quello che hai suggerito, qualcosa del genere esiste già. Se desideri fornire questa funzione ai tuoi progetti Java, puoi prendere in considerazione l'utilizzo di Lombok . Ha un @Builder annotation , che ti consente di creare un builder con praticamente una sola riga di codice e al allo stesso tempo evita di esporre o addirittura di scrivere setter.

Ecco come può essere utilizzato:

import lombok.Builder;

@Builder
public class Person {

    private String name;

    private int age;

    public String getName() { return name; }

    public int getAge() { return age; }
}

Per impostare i campi su un'istanza di questa classe, chiameresti:

Person instance = Person.builder().name("Tom").age(25).build();

In effetti, se decidi di usare Lombok, puoi farlo generare i getter per te e rendere il codice ancora più breve:

import lombok.Builder;
import lombok.Getter;

@Builder
public class Person {

    @Getter
    private String name;

    @Getter
    private int age;
}

Gli svantaggi includono la necessità di configurare Lombok per il tuo progetto e includere una dipendenza da esso. Ho lavorato su un paio di progetti e posso dire che è relativamente indolore quando si tratta di configurare l'ambiente.

Penso che lo svantaggio peggiore sia quello delle funzionalità di debug limitate (dato che le origini originali non conterranno le linee effettivamente eseguite).

Ad ogni modo, è lì fuori che puoi provare a realizzare i pro e i contro di tale soluzione.

Un'altra opzione per avere questo tipo di zucchero sintattico sarebbe passare a Groovy, che ha una curva di apprendimento piuttosto delicata per gli sviluppatori Java e supporto molto bello anche per i costruttori grazie alle sue trasformazioni AST .

    
risposta data 26.04.2015 - 16:19
fonte
1

Non impedirebbe alle persone di avere:

s.set().parameter1(value);

// yadda yadda yadda

s.set().parameter2(anotherValue);

Questo è il motivo per cui hai disapprovato, quindi direi che non aumenterebbe la qualità del codice da solo.

Il pattern Builder - che tu menzioni - impone il passaggio di tutti i valori in un posto (o il rinvio della costruzione dell'oggetto a meno che non siano tutti dentro), ma ciò è anche ottenibile avendo un costruttore che si aspetta che ogni singolo valore venga passato direttamente via, più immutabilità - quindi, tutti i getter e nessun setter.

I fluenti builder sono una sorta di zucchero di sintassi per questo, equivalente ai parametri denominati come: link

Se intendi generatori automatici per classi di builder, piuttosto che classi di entità con setter concatenabili, allora sì esistono, ad es. link

Personalmente non vedo molto nel loro utilizzo, dato che IntelliJ Idea o Android Studio sono in grado di generare automaticamente una classe di builder in pochi secondi.

    
risposta data 24.04.2015 - 16:29
fonte
0

JDK 1.8 fornisce un approccio molto carino (dipende dal tuo giudizio) per usare Fluent Pattern, controlla questo esempio (Usando la classe Person dall'esempio qui):

import java.util.function.Consumer;

public class Person {
   private String name;
   private String lastName;
   private int age;
   private Genre genre; // assuming enum MALE/FEMALE

   // Some GETs

   // Fluent SETTERS
   public Person setName(String name) {
      this.name = name;
      return this;
   }
   public Person setLastName(String lastName) {
      this.lastName = lastName;
      return this;
   }
   public Person setAge(int age) {
      this.age = age;
      return this;
   }
   public Person setGenre(Genre genre) {
      this.genre = genre;
      return this;
   }
   // Beautiful things start just here
   public static Person build(Consumer<Person> block) {
      Person person = new Person(); // constructor may be private :)
      block.accept(person);
      return person;
   }
}

Uso del "builder":

public class JDK8ConsumerExample {
   public static void main(String ... args) {
      Person person = Person.build(p ->
         p.setNane("Jhon")
          .setLastName("Doe")
          .setAge(40)
          .setGenre(Genre.MALE)
        );
   }
}

Un tutorial molto bello da Mr. Venkat Subramaniam qui

    
risposta data 05.04.2017 - 20:47
fonte

Leggi altre domande sui tag