How many constructor arguments is too many?

ParametersRefactoringConstructorCoding Style

Parameters Problem Overview


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?

Parameters Solutions


Solution 1 - Parameters

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

Solution 2 - Parameters

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.

Solution 3 - Parameters

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] 1

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

Solution 4 - Parameters

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

Solution 5 - Parameters

If you have unpalatably many arguments, then just package them together into structs / POD classes, preferably declared as inner classes of the class you are constructing. That way you can still require the fields while making the code that calls the constructor reasonably readable.

Solution 6 - Parameters

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.

Solution 7 - Parameters

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.

Solution 8 - Parameters

Steve Mcconnell writes in Code Complete that people have trouble keeping more 7 things in their head at a time, so that'd be the number I try to stay under.

Solution 9 - Parameters

I'd encapsulate similar fields into an object of its own with its own construction/validation logic.

Say for example, if you've got

  • BusinessPhone
  • BusinessAddress
  • HomePhone
  • HomeAddress

I'd make a class that stores phone and address together with a tag specifying wether its a "home" or a "business" phone/address. And then reduce the 4 fields to merely an array.

ContactInfo cinfos = new ContactInfo[] {
    new ContactInfo("home", "+123456789", "123 ABC Avenue"),
    new ContactInfo("biz", "+987654321", "789 ZYX Avenue")
};

Customer c = new Customer("john", "doe", cinfos);

That should make it look less like spaghetti.

Surely if you have a lot of fields, there must be some pattern you can extract out that would make a nice unit of function of its own. And make for more readable code too.

And the following is also possible solutions:

  • Spread out the validation logic instead of storing it in a single class. Validate when user input them and then validate again at the database layer etc...
  • Make a CustomerFactory class that would help me construct Customers
  • @marcio's solution is also interesting...

Solution 10 - Parameters

Style counts for a lot, and it seems to me that if there is a constructor with 20+ arguments, then the design should be altered. Provide reasonable defaults.

Solution 11 - Parameters

I would think the easiest way would be to find an acceptable default for each value. In this case, each field looks like it would be required to construct, so possibly overload the function call so that if something is not defined in the call, to set it to a default.

Then, make getter and setter functions for each property so that the default values could be changed.

Java implementation:

public static void setEmail(String newEmail){
    this.email = newEmail;
}

public static String getEmail(){
    return this.email;
}

This is also good practice to keep your global variables secure.

Solution 12 - Parameters

Just use default arguments. In a language that supports default method arguments (PHP, for example), you could do this in the method signature:

public function doSomethingWith($this = val1, $this = val2, $this = val3)

There are other ways to create default values, such as in languages that support method overloading.

Of course, you could also set default values when you declare the fields, if you deem it appropriate to do so.

It really just comes down to whether or not it is appropriate for you to set these default values, or if your objects should be specced out at construction all the time. That's really a decision that only you can make.

Solution 13 - Parameters

I agree on the 7 item limit Boojiboy mentions. Beyond that, it may be worth looking at anonymous (or specialized) types, IDictionary, or indirection via primary key to another data source.

Solution 14 - Parameters

In a more Object-Oriented situation of the problem, you can use properties in C#. It doesn't help much if you create an instance of an object, but suppose we have a parent class that needs too many parameters in its constructor.
Since you can have abstract properties, you can use this to your advantage. The parent class needs to define an abstract property that the child class must override.
Normally a class might look like:

class Customer {
    private string name;
    private int age;
    private string email;

    Customer(string name, int age, string email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

class John : Customer {
    John() : base("John", 20, "[email protected]") { 

    }
}

It can become messy and unreadable with too many parameters.
Whereas this method:

class Customer {
    protected abstract string name { get; }
    protected abstract int age { get; }
    protected abstract string email { get; }
}

class John : Customer {
    protected override string name => "John";
    protected override int age => 20;
    protected override string email=> "[email protected]";
}

Which is much cleaner code in my opinion and no contractors are needed in this case, which saves room for other necessary parameters.

Solution 15 - Parameters

Unless it's more than 1 argument, I always use arrays or objects as constructor parameters and rely on error checking to make sure the required parameters are there.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevin PangView Question on Stackoverflow
Solution 1 - ParameterstoolkitView Answer on Stackoverflow
Solution 2 - ParametersKeith PinsonView Answer on Stackoverflow
Solution 3 - ParametersMarcio AguiarView Answer on Stackoverflow
Solution 4 - ParametersOwenPView Answer on Stackoverflow
Solution 5 - ParametersMcKenzieG1View Answer on Stackoverflow
Solution 6 - ParametersEthan GundersonView Answer on Stackoverflow
Solution 7 - ParametersvituleView Answer on Stackoverflow
Solution 8 - ParametersBooji BoyView Answer on Stackoverflow
Solution 9 - ParameterschakritView Answer on Stackoverflow
Solution 10 - ParametersAdam HollidgeView Answer on Stackoverflow
Solution 11 - ParametershelloandreView Answer on Stackoverflow
Solution 12 - ParametersBrian WarshawView Answer on Stackoverflow
Solution 13 - ParametersspoulsonView Answer on Stackoverflow
Solution 14 - ParametersRuanView Answer on Stackoverflow
Solution 15 - ParametersAldieView Answer on Stackoverflow