Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How many constructor arguments is too many? [closed]

People also ask

How many arguments should a constructor have?

This method has four parameters: the loan amount, the interest rate, the future value and the number of periods.

How many arguments is too much?

Functions with three arguments (triadic function) should be avoided if possible. More than three arguments (polyadic function) are only for very specific cases and then shouldn't be used anyway.

How many arguments we can pass in constructor?

This method takes four arguments: the loan amount, the interest rate, the future value and the number of periods. The first three are double-precision floating point numbers, and the fourth is an integer.

How many maximum parameters we can use with difficult constructor?

Sometimes overloaded constructors are also used. But Checkstyle points out that the maximum number of arguments given to a method or constructor must be 7.


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();
    }
}

I see that some people are recommending seven as an upper limit. Apparently it is not true that people can hold seven things in their head at once; they can only remember four (Susan Weinschenk, 100 Things Every Designer Needs to Know about People, 48). Even so, I consider four to be something of a high earth orbit. But that's because my thinking has been altered by Bob Martin.

In Clean Code, Uncle Bob argues for three as an general upper limit for number of parameters. He makes the radical claim (40):

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic) followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn't be used anyway.

He says this because of readability; but also because of testability:

Imagine the difficulty of writing all the test cases to ensure that all various combinations of arguments work properly.

I encourage you to find a copy of his book and read his full discussion of function arguments (40-43).

I agree with those who have mentioned the Single Responsibility Principle. It is hard for me to believe that a class that needs more than two or three values/objects without reasonable defaults really has only one responsibility, and would not be better off with another class extracted.

Now, if you are injecting your dependencies through the constructor, Bob Martin's arguments about how easy it is to invoke the constructor do not so much apply (because usually then there is only one point in your application where you wire that up, or you even have a framework that does it for you). However, the Single Responsibility Principle is still relevant: once a class has four dependencies, I consider that a smell that it is doing a large amount of work.

However, as with all things in computer science, there are doubtless valid cases for having a large number of constructor parameters. Don't contort your code to avoid using a large number of parameters; but if you do use a large number of parameters, stop and give it some thought, because it may mean your code is already contorted.


In your case, stick with the constructor. The information belongs in Customer and 4 fields are fine.

In the case you have many required and optional fields the constructor is not the best solution. As @boojiboy said, it's hard to read and it's also hard to write client code.

@contagious suggested using the default pattern and setters for optional attributs. That mandates that the fields are mutable, but that's a minor problem.

Joshua Block on Effective Java 2 say that in this case you should consider a builder. An example taken from the book:

 public class NutritionFacts {  
   private final int servingSize;  
   private final int servings;  
   private final int calories;  
   private final int fat;  
   private final int sodium;  
   private final int carbohydrate;  

   public static class Builder {  
     // required parameters  
     private final int servingSize;  
     private final int servings;  

     // optional parameters  
     private int calories         = 0;  
     private int fat              = 0;  
     private int carbohydrate     = 0;  
     private int sodium           = 0;  

     public Builder(int servingSize, int servings) {  
      this.servingSize = servingSize;  
       this.servings = servings;  
    }  

     public Builder calories(int val)  
       { calories = val;       return this; }  
     public Builder fat(int val)  
       { fat = val;            return this; }  
     public Builder carbohydrate(int val)  
       { carbohydrate = val;   return this; }  
     public Builder sodium(int val)  
       { sodium = val;         return this; }  

     public NutritionFacts build() {  
       return new NutritionFacts(this);  
     }  
   }  

   private NutritionFacts(Builder builder) {  
     servingSize       = builder.servingSize;  
     servings          = builder.servings;  
     calories          = builder.calories;  
     fat               = builder.fat;  
     soduim            = builder.sodium;  
     carbohydrate      = builder.carbohydrate;  
   }  
}  

And then use it like this:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
      calories(100).sodium(35).carbohydrate(27).build();

The example above was taken from Effective Java 2

And that doesn't only applies to constructor. Citing Kent Beck in Implementation Patterns:

setOuterBounds(x, y, width, height);
setInnerBounds(x + 2, y + 2, width - 4, height - 4);

Making the rectangle explicit as an object explains the code better:

setOuterBounds(bounds);
setInnerBounds(bounds.expand(-2));

I think the "pure OOP" answer is that if operations on the class are invalid when certain members aren't initialized, then these members must be set by the constructor. There's always the case where default values can be used, but I'll assume we're not considering that case. This is a good approach when the API is fixed, because changing the single allowable constructor after the API goes public will be a nightmare for you and all users of your code.

In C#, what I understand about the design guidelines is that this isn't necessarily the only way to handle the situation. Particularly with WPF objects, you'll find that .NET classes tend to favor parameterless constructors and will throw exceptions if the data has not been initialized to a desirable state before calling the method. This is probably mainly specific to component-based design though; I can't come up with a concrete example of a .NET class that behaves in this manner. In your case, it'd definitely cause an increased burden on testing to ensure that the class is never saved to the data store unless the properties have been validated. Honestly because of this I'd prefer the "constructor sets the required properties" approach if your API is either set in stone or not public.

The one thing I am certain of is that there are probably countless methodologies that can solve this problem, and each of them introduces its own set of problems. The best thing to do is learn as many patterns as possible and pick the best one for the job. (Isn't that such a cop-out of an answer?)


I think it all depends on the situation. For something like your example, a customer class, I wouldn't risk the chance of having that data being undefined when needed. On the flip side, passing a struct would clear up the argument list, but you would still have a lot of things to define in the struct.


I think your question is more about the design of your classes than about the number of arguments in the constructor. If I needed 20 pieces of data (arguments) to successfully initialize an object, I would probably consider breaking up the class.