5

So I have a webproject with Hybris, Spring and so on.

I have some classes, which are autogenerated. Let's say I have one modelclass, which is autogenerated and inherits from another class some methods, to set fields.

When writing Unit-tests, is it useful to start using the Builder pattern? Because the thing is, I don't have a constructor, like Employee(int id, String name) and so on, I only have the inherited methods to set them (setId(int id) and so on).

So when I would write a Builder class for this model for example, I would have the methods .withId(int id) and .withName(String name) and the build()-method, where I would run the setter-methods.

so in the end in my test-class I would have:

EmployeeBuilder eb = new EmployeeBuilder();
Employee emp = eb.withId(123)
                 .withName("John")
                 .build();

But since I already have Setter-Methods I normally have:

Employee emp = new Employee();
emp.setId(123);
emp.setName("John");

So is it really worth the effort in this case? Or is there something I have not really understood?

Thanks!

user5417542
  • 3,146
  • 6
  • 29
  • 50
  • 1
    I would say if the code is easy to understand to the naked eye, do what works for you better and do not care about what Paradigm zealots say(because in the end it always ends up being "My paradigm X is better than your paradigm Y" fight).This is one way to keep your sanity and not be put off the language completely. – The Law Oct 20 '15 at 07:18
  • 1
    The build method could perform verifications on the object before creating an instance. – JEY Oct 20 '15 at 07:19
  • 2
    If you already have setter methods, no, it is not useful. I would tend to use the Builder pattern for constructing immutable objects with large numbers of properties, which obviously don't have setters (or, at least, where there are some immutable properties, so those properties don't have setters). – Andy Turner Oct 20 '15 at 07:19
  • 2
    @JEY surely the existing setters would also need to perform verification. – Andy Turner Oct 20 '15 at 07:27
  • @Andy, ofc it is usefull, sigh. Less code to write, you get rid of the repetitive "em.set" part as you nicely can chain the messages. At user5417542, the pattern makes more sense when you have nested objects inside each other and not only simple fields like ints and strings. If you mainly want this in tests, consider to switch to the Spock testing Framework, and script your tests in Groovy. – Angel O'Sphere Oct 21 '15 at 12:06
  • 1
    @AngelO'Sphere adding a builder class when you already have setters isn't less code to write: you are clearly duplicating code. If you want to chain the method calls, nothing stops you returning `this` from the setters: method chaining isn't necessary to use the builder pattern, nor is it exclusive to the builder pattern. – Andy Turner Oct 21 '15 at 12:22
  • @Andy, of it is. I don't get why you argue about that. I likely will use the Builder in dozens of tests, and probably in production code where it is aprobiated. On top of that: I generate my builders via the IDE and some reflection. It is a click of the mouse and then copying the clipboard into a Java file. Having the Setters return this is an Option, too. Depending on the projects I indeed configure the IDE that auto generated Setters return this. However you often want tests/builders for code you have no access to. – Angel O'Sphere Oct 21 '15 at 14:40

4 Answers4

6

Before I give an answer to your question I would like to explain the builder pattern.

The builder pattern is usually used when you have a lot of overloaded constructors (telescoping constructor anti-pattern). E.g.

public class Employee {

   public Employee(String firstName, String lastName){
       ...
   }

   public Employee(String firstName, String lastName, Sex sex){
       ...
   }


   public Employee(String firstName, String lastName, String salutation) {
       ...
   }
}

In this case client code must decide which constructor to invoke depending on the data it has. If it has a firstName and lastName it must invoke new Employee(firstName, lastName). If it only has a firstName it must invoke Employee(String firstName). So the client code might have a lot of if/then/else. E.g.

Employee employee = null;
if(firstName != null && lastName != null && sex != null){
    employee = new Employee(firstName, lastName, sex);
} else if(firstName != null && lastName != null && salutation != null){
    employee = new Employee(firstName, lastName, salutation );
} else {
  .....
}

The design of the Employee class in this example includes that firstName and lastName are mandatory attributtes of an Employee, because every constructor needs them. The attributes sex and saluation are optional. If the client code decides which constructor to invoke this also means that the decision process is duplicated accross client code. E.g. if a client knows the firstName, lastName, sex and salutation which constructor should it call? Either new Employee(firstName, lastName, sex) or new Employee(firstName, lastName, saluation)?

In order to encapsulate the constructor resolution you might want to use a builder pattern.

public class EmployeeBuilder {

      public EmployeeBuilder(String firstName, String lastName){

      }

      public void setSex(Sex sex){ ... }

      public void setSalutation(Salutation salutation){ ... }

      public Employee build(){
          if(salutation != null){
             return new Emplyoee(firstName, lastName, salutation);
          } else if(sex != null){
             return new Emplyoee(firstName, lastName, sex); 
          } else {
             return new Emplyoee(firstName, lastName);
          }
      }
}

This makes the client-code much easier to read and the constructor invokation decision is encapsulated. E.g.

EmployeeBuidler employeeBuilder = new EmployeeBuilder(firstName, lastName);

Sex sex = ...; 
String salutation = ...;

employeeBuilder.setSex(sex);
employeeBuilder.setSalutation(salutation);

Employee employee = employeeBuilder.build();

Back to your question

So is it really worth the effort in this case?

For your unit tests you might want to create Employee objects with some attributes and the others should be set to default values. In this case I think it is a good idea to use a builder pattern. I would name the builder then e.g. EmployeeDefaultValuesBuilder to make it clear.

You might also want to build Employees based on other employee objects (templates). In this case I would add another constructor to the EmployeeBuilder. E.g.

public EmployeeBuilder(Employee template){
  // initialize this builder with the values of the template
}

So it is worth the effort if you encapsulate construction logic or if it increases readability.

René Link
  • 48,224
  • 13
  • 108
  • 140
5

Builder pattern is useful for:

  • Immutable classes, which is not the case here.
  • When you need to build many of the same things with minor differences. Which is also not the case here.
  • Writing a "Fluent" API.
  • When you have a complex object which requires a complex build.

So is it really worth the effort in this case?

Given what you have posted, I'd say no.

Finally the effort involved is tiny when using the right APIs, such as Project Lombok or Google Auto. (Also if you are using a builder to hide a telescoping constructor anti-pattern I think you are abusing the pattern, but hey...)

Michael Lloyd Lee mlk
  • 14,561
  • 3
  • 44
  • 81
2

Builder pattern is useful in two cases:

  • resulting object is immutable(all fields are final) - builder is better than constructor with many arguments.
  • you want to be sure that created object is valid and no inconsistent objects can be created - for example you can throw error from build() method if longitude field was set but latitude was not.
user158037
  • 2,659
  • 1
  • 24
  • 27
  • 3
    "you can throw error from build() method if longitude field was set but latitude was not" It would be better to require both at the same call to the builder - e.g. `setLatitudeAndLongitude` - so that it becomes a compile-time error not to provide both, rather than waiting to find out at runtime. – Andy Turner Oct 21 '15 at 12:29
1

As you demonstrate in your code example, with the builder pattern you only save a couple time writing out your variable name emp, but also have to add a final build() call or similar.

In my book that certainly does not pay off the investment of creating additional builders.

But ...

Maybe you have fields that need to be filled, but that are really not relevant for what you want to test.

Or you want to create multiple instances that only differ in few properties.

Those can be build nicely in a builder, saving lots of lines of code, and what is more important, makes your tests much clearer.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348