4
votes

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!

4
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
The build method could perform verifications on the object before creating an instance. - JEY
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
@JEY surely the existing setters would also need to perform verification. - Andy Turner
@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

4 Answers

6
votes

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.

4
votes

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

2
votes

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.
1
votes

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.