views:

1254

answers:

13

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?

+1  A: 

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.

Aldie
+2  A: 

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.

McKenzieG1
A: 

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.

Brian Warshaw
+2  A: 

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.

Adam Hollidge
+1  A: 

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.

Ethan Gunderson
+2  A: 

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.

contagious
+2  A: 

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.

Booji Boy
A: 

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.

spoulson
+1  A: 

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

OwenP
+2  A: 

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.

vitule
+15  A: 

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 {
    private String surname;
    private String firstName;
    private String ssn;
    public static CustomerBuilder customer() {
        return new CustomerBuilder();
    }
    public CustomerBuilder withSurname(String surname) {
        this.surname = surname;
        return this;
    }
    public CustomerBuilder withLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }
    public CustomerBuilder withSSN(String ssn) {
        this.ssn = ssn;
        return this;
    }

    // client doesn't get to instantiate Customer directly
    public Customer build() {
        if (surname == null || firstName == null || ssn == null) {
            throw new NullPointerException("Incomplete customer definition");
        }
        /* option 1: would have package private setters */
        Customer target = new Customer();
        target.setSurname(this.surname);
        target.setFirstName(this.firstName);
        target.setSSN(this.ssn); 
        return target;

        /* option 2:

        I actually like the approach marcio suggests, where we pass the
        builder in the constructor - prevents the need for all of the
        setters

        Customer target = new Customer(this);
        return target;
        */            
    }
}


import static com.acme.CustomerBuilder.customer;

public class Client {
    public void doSomething() {
        Customer customer = customer()
            .withSurname("Smith")
            .withFirstName("Fred")
            .withSSN("123XS1")
            .build();
    }
}
toolkit
I know this as the "Named Parameter Idiom": http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18.Related: There is also the "Named Constructor Idiom": http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8
can you separate the caller and callee segments of code to make it more clear they are separate entities?
Chris Kaminski
The check for null values (surname == null || firstName == null || ssn == null) should be done ON THE Customer object. This is due to the possibility of these values being modified before the Customer object is created. Furthermore make sure the fields in the Customer class are all declared to be "private final" - making the Customer immutable.
drozzy
+3  A: 

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));
Marcio Aguiar
Of course if all the arguments are Required in the constructor, you just end up moving one huge constructor from one place to another.
drozzy
A: 

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...
chakrit