6

Consider that code:

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

It is really naive, so we can use a builder pattern:

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

It is okay, but what if we have to use a lot of setters during a program?

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

It will be more efficient and less boilerplated:

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

Looks more okay and it allows programmer not do this action: (i hate it):

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

even there is no neccessary to put this setters away (because it is a some legacy issue, somebody just tap the enter twice and someone type in a other code...

What if...

@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...

Cool stuff or no? Is this solution would significally increase a code quality or not and its a wreck and why?

4 Answers4

5

While not exactly the same as what you suggested, something along these lines already exists. If you want to provide this feature to your Java projects, you may consider using Lombok. It has a @Builder annotation, which allows you to create a builder with pretty much a single line of code and at the same time avoid exposing or even writing setters.

Here's how it can be used:

import lombok.Builder;

@Builder
public class Person {

    private String name;

    private int age;

    public String getName() { return name; }

    public int getAge() { return age; }
}

To set the fields on an instance of this class, you would now call:

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

In fact, if you decide to use Lombok, you can have it generate the getters for you and make the code even shorter:

import lombok.Builder;
import lombok.Getter;

@Builder
public class Person {

    @Getter
    private String name;

    @Getter
    private int age;
}

The downsides include the necessity to set up Lombok for your project and include a dependency on it. I've worked with it on a couple of project and I can say it's relatively painless when environment setup is concerned.

I think the worst drawback is that of limited debugging capabilities (as your original sources will not contain the lines that are actually run).

Anyway, it's out there for you to try and realize the pros and cons of such a solution.

Another option to have this kind of syntactic sugar would be to switch to Groovy, which has quite a gentle learning curve for Java developers and very nice support for builders as well thanks to its AST transformations.

toniedzwiedz
  • 1,353
3

The Builder pattern addresses a particular problem that arises - those constructors with a dozen (or more!) arguments. Yes, there's often another problem lurking with such objects, but the Builder makes it so that one doesn't need too many constructors to handle all the different variations on how the object can be constructed.

A fluent style setter (where the setter returns this), doesn't have this issue. Other than some stylistic annoyances to it, I don't see a problem that needs to be solved.

Having a compile time annotation, while neat, cool, and powerful adds a significant amount of complexity to the build of the project and makes it difficult for someone (or something) reading the code to reason about it. Sure, you can annotate it and have the compiler do its magic... but there is often something before the compiler (aside fro the coder) who needs to deal with the code.

The "something" reading the code is a real concern here. The IDE doesn't know about the method set(). Nor does it know what it returns, nor all of the methods that it has. This would make the IDE's view of the class a mass of red "this does not exist" markers.

The set() approach is also far from standard (and doesn't actually solve a real problem) and makes it harder for a new coder to come to the code base and understand it. It adds unnecessary complexity to the build process and can make the IDE unhappy.

While all of this may be cool and clever (yes, I think annotations are cool and clever and have a love / hate relationship with them myself), I don't think that this is a good path to follow.

1

It wouldn't prevent people from having:

s.set().parameter1(value);

// yadda yadda yadda

s.set().parameter2(anotherValue);

Which is what you frowned upon in the first place, so I'd say it wouldn't increase code quality by itself.

Builder pattern - that you mention - does enforce passing all values in one place (or deferring construction of the object unless they're all in), but that's also accomplishable by having a constructor that expects every single value to be passed straight away, plus immutability - so, all getters and no setters.

Fluent builders are sort of syntax sugar for that, equivalent to named parameters like: Link

If you mean automatical generators for builder classes, rather than entity classes with chainable setters, then yes they do exist, eg. https://github.com/mkarneim/pojobuilder

Personally I don't see much point in using them, given that IntelliJ Idea or Android Studio is capable of automatically generating a builder class in a matter of seconds.

Glorfindel
  • 3,167
0

JDK 1.8 provide a very nice approach (depends on your judgment) to use Fluent Pattern, check this example (Using Person class from example in here):

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

Using the "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)
        );
   }
}

A very nice tutorial from Mr. Venkat Subramaniam here

Vielinko
  • 101