7

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?

  • I would point out that the way you phrased the question, and that its getting answered is one of a resource request. This typically doesn't generate good long term content for the site. As of this writing, its also getting unclear close votes. I would strongly suggest that you rephrase the question to clearly explain what your design is, and the problem you are having with it. – user40980 Apr 24 '15 at 14:06
  • Hm, you are right - don't get me wrong, its not a point to discuss about are there any significant solutions (and naming it) but rather is it more acceptable than an old way? I must edit the question though, sorry. – Dawid Pura Apr 24 '15 at 14:18
  • 1
    @DawidPura Meh, I'm not a big believer in setting parameter post-constructor unless there is absolutely no other way. Better if instances are immutable if it can be helped. – Neil Apr 24 '15 at 14:40
  • 2
    Project Lombok has (along with lots of other great annotations) @Builder. – Mike Partridge Apr 24 '15 at 14:43
  • 1
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.

  • You are right. The idea maybe makes sense, but IDE won't recognize methods, and Java has not any macros - it wouldn't work. Thanks, you gave me cool answer! – Dawid Pura Apr 24 '15 at 15:10
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.

  • Might want to provide a link: projectlombok.org/features/Builder.html – Idan Arye Apr 26 '15 at 15:28
  • @IdanArye I did, it's just not very visible in this site's layout. The word @Builder is a link. – toniedzwiedz Apr 26 '15 at 15:39
  • 1
    @IdanArye made it more visible now. – toniedzwiedz Apr 26 '15 at 15:46
  • Since you have brought Lombok into the picture, you should eliminate the get methods and use @Getter. – kevin cline Jan 1 '16 at 19:52
  • @kevincline I just introduced the Getter annotation in a separate code snippet. Thanks for the comment. – toniedzwiedz Jan 1 '16 at 21:20
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: http://geekswithblogs.net/michelotti/archive/2009/01/22/c-4.0-named-parameters-for-better-code-quality.aspx

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.

  • Fully agree, that would not prevent from setting separately. But, from longer perspective, if developer would use chain setter instead of regular setters, other developer will not add other code between two settings methods (it happens). – Dawid Pura Apr 24 '15 at 14:52
  • 1
    @DawidPura they wouldn't squeeze more code between what's already there, but they would still add some of their own a mile away. – Konrad Morawski Apr 24 '15 at 15:16
  • There are two strong reasons to NOT use IDE code generation: 1. the generated code is just noise to future readers, and 2. the generated code doesn't maintain itself. – kevin cline Jan 1 '16 at 19:54
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

  • The sample Person class won't compile. Return statements are missing in setters. – topr Apr 12 '18 at 13:00
  • @topr, your were right! even when I just tried to show how-to use the pattern, already edited in case someone else got confused. – Vielinko May 8 '18 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.