How many constructor arguments is too many?

前端 未结 15 1186
[愿得一人]
[愿得一人] 2020-11-29 15:03

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

  • UserName
  • Email
  • First Name
  • Last Name

相关标签:
15条回答
  • 2020-11-29 15:44

    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.

    0 讨论(0)
  • 2020-11-29 15:44

    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.

    0 讨论(0)
  • 2020-11-29 15:44

    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.

    0 讨论(0)
  • 2020-11-29 15:45

    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));
    
    0 讨论(0)
  • 2020-11-29 15:47

    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.

    0 讨论(0)
  • 2020-11-29 15:51

    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.

    0 讨论(0)
提交回复
热议问题