How many constructor arguments is too many

coding-styleconstructorparametersrefactoring

Let's say you have a class called Customer, which contains the following fields:

  • UserName
  • Email
  • First Name
  • Last Name

Let's also say that according to your business logic, all Customer objects must have these four properties defined.

Now, we can do this pretty easily by forcing the constructor to specify each of these properties. But it's pretty easy to see how this can spiral out of control when you are forced to add more required fields to the Customer object.

I've seen classes that take in 20+ arguments into their constructor and it's just a pain to use them. But, alternatively, if you don't require these fields you run into the risk of having undefined information, or worse, object referencing errors if you rely on the calling code to specify these properties.

Are there any alternatives to this or do you you just have to decide whether X amount of constructor arguments is too many for you to live with?

Best Answer

Two design approaches to consider

The essence pattern

The fluent interface pattern

These are both similar in intent, in that we slowly build up an intermediate object, and then create our target object in a single step.

An example of the fluent interface in action would be:

public class CustomerBuilder {
    String surname;
    String firstName;
    String ssn;
    public static CustomerBuilder customer() {
        return new CustomerBuilder();
    }
    public CustomerBuilder withSurname(String surname) {
        this.surname = surname; 
        return this; 
    }
    public CustomerBuilder withFirstName(String firstName) {
        this.firstName = firstName;
        return this; 
    }
    public CustomerBuilder withSsn(String ssn) {
        this.ssn = ssn; 
        return this; 
    }
    // client doesn't get to instantiate Customer directly
    public Customer build() {
        return new Customer(this);            
    }
}

public class Customer {
    private final String firstName;
    private final String surname;
    private final String ssn;

    Customer(CustomerBuilder builder) {
        if (builder.firstName == null) throw new NullPointerException("firstName");
        if (builder.surname == null) throw new NullPointerException("surname");
        if (builder.ssn == null) throw new NullPointerException("ssn");
        this.firstName = builder.firstName;
        this.surname = builder.surname;
        this.ssn = builder.ssn;
    }

    public String getFirstName() { return firstName;  }
    public String getSurname() { return surname; }
    public String getSsn() { return ssn; }    
}
import static com.acme.CustomerBuilder.customer;

public class Client {
    public void doSomething() {
        Customer customer = customer()
            .withSurname("Smith")
            .withFirstName("Fred")
            .withSsn("123XS1")
            .build();
    }
}