Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Builder Pattern useful with Setter-methods?

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!

like image 755
user5417542 Avatar asked Dec 10 '22 20:12

user5417542


1 Answers

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.

like image 135
René Link Avatar answered Dec 28 '22 07:12

René Link