views:

1723

answers:

18

Hi, I can't decide which approach is better for creating objects with a large number of fields (10+) (all mandatory) the constructor approach of the getter/setter. Constructor at least you enforce that all the fields are set. Java Beans easier to see which variables are being set instead of a huge list. The builder pattern DOES NOT seem suitable here as all the fields are mandatory and the builder requires you put all mandatory parameters in the builder constructor.

Thanks, D

+2  A: 

IMHO, you should pass everything that is needed for an object to be valid according to your business logic in the constructor.

If the argument list is lengthy, you could create an object that contains the arguments and pass that.

JRL
An object like a builder?
Tom Hawtin - tackline
How are you going to construct the object that contains the arguments? It's turtles all the way down!
Peter Recore
+19  A: 

My first thought is to check whether your encapsulation model is correct. Having 10+ mandatory fields sounds like quite a lot and perhaps it makes more sense to have more finely-grained components in this scenario ?

Are some of these fields/parameters related ? Can they be combined into objects that make sense (e.g. x-coordinate and y-coordinate combined into a Point object etc.)

Brian Agnew
+22  A: 

The better approach (imho) is to use some kind of builder:

MyClass a = new MyClassBuilder().blah("blah").foo("foo").doStuff().toMyClass();

where MyClass is still immutable but has a far more readable creation than a constructor with 10 arguments.

This is also called a fluent interface. Josh Bloch refers to this in Effective Java.

cletus
this is also documented in Effective Java
dfa
I would strongly disagree, given that all ten fields are mandatory.If each field was optional and acted as a modifier (ie: Hibernate's SQLQuery), I'd agree, using a fluent interface would be best, but when all ten are required, the code is going to be massive, and it's going to allow creating an object that isn't complete.
Milan Ramaiya
It depends on how "mandatory" the "mandatory" fields are. If they simply need to be set to sensible defaults, the builder pattern is good for that. If each one needs to be set very specifically to a unique value, then the builder could indeed become unwieldy.
Spike Williams
Put 10 strings together even when they're all mandatory and errors are likely plus it can be hard to read. This can be adapted as necessary. For example, call a function with 2-4 logically grouped arguments. Methods with more than about 4 arguments are undesirable most of the time. Unfortunately Java (unlike, say, Javascript) doesn't have a convenient method of creating anonymous objects to handle this situation.
cletus
The builder pattern passes all the mandatory arguments to the constructor of the builder so I dont see how that helps...
deelo55
Because the construction becomes an internal implementation matter rather than an external interface matter. What would you rather have: 100 pieces of code with a readable builder and 1 messy internal constructor or 100 messy 10 arg constructors?
cletus
I think the comments are misunderstanding the Builder pattern. If you need immutability, see the link at my answer that puts it as a nested class, but the Builder can be in the same package as the object, and more importantly can validate on the call to the build method that all the parameters were set. The builder object is a different object from the class so the fluent interface doesn't create an incomplete object at any stage.
Yishai
@Reverend Gonzo the build method (or toMyClass in this example) can validate that all required properties are present, so that you don't get an incomplete object.
Jason Day
@deelo55 if your constructor has 10 args, it will be very difficult to remember what position each arg is in (e.g. is the font name the fourth arg or the fifth?). With a builder, you get a reasonable substitute for named parameters, so you don't have to remember the position of each arg.
Jason Day
+4  A: 

You might consider using a builder pattern, with the builder ensuring that all the fields are at least set to sensible defaults. Refer to the link for implementation, but you would wind up with a call that looks something like:

Widget widge = new Widget.Builder(). manufacturer("333").serialNumber("54321").build();
Spike Williams
+8  A: 

I would recommend you consider the builder pattern in such a case. You are guaranteed to get a valid object, without just having a huge list of parameters.

The OP was update to reject the builder pattern, but it seems to be based on a misunderstanding. The fact that the Builder pattern exists does not remove the enforcement of all the parameters.

Consider the following object:

 public class SomeImmutableObject {
      private String requiredParam1;
      private String requiredParam2;
      //etc.

      private SomeImmutableObject() { //cannot be instantiated outside the class }

      public static class Builder {
          private SomeImmutableObject instance;
          public Builder() { instance = new SomeImmutableObject();
          public Builder setParameter1(String value) {
               instance.requiredParam1 = value;
               return this;
          }
          //etc for each parameter.

          public SomeImmutableObject build() {
             if (instance.requiredParam1 == null || instance.requiredParam2 == null /*etc*/)
                throw new IllegalStateException("All required parameters were not supplied.");
             return instance;
          }
      } 
 }

Note that you can accomplish basically the same thing by making the fields package private and putting the builder in the same package.

And if for some reason you can't do that, you can still have the constructor with the 10 parameters, and then have the Builder be the only thing that calls that constructor, so that it is an easier API to use.

So for all stated requirements, the Builder pattern works just fine. The fact that all 10 parameters are required does not disqualify the Builder pattern at all. If there is some other need that the pattern doesn't satisfy, please elaborate.

Edit: The OP added a comment (quite a while ago, but I just got an upvote on this question so I only saw it now) with an interesting question: How do you validate a primitive at a later point in time?

There are a few ways around that problem, including a guarding boolean, but my preferred way would be to use a Double object like so:

     private Double doubleForPrimitive;

     public Builder setDouble(double d) {
         doubleForPrimitive = d;
     }

     public SomeImmutableObject build() {
         if(doubleForPrimitive != null) {
               instance.doubleParam = doubleForPrimitive;
         } else {
                throw new IllegalArgumentExcepion("The parameter double was not provided");
         }
         //etc.
     }

It should be noted that if you need true thread-safe immutability having all of the fields of the immutable object as final, this requires more boilerplate (storing the variables inside the builder and passing them to a private constructor of the immutable object), but the approach is still clean from the point of view of the client code.

Yishai
+1  A: 

Really depends on the specific class. Should it be immutable? Is it a simple value object without any behavior? Are you going to map this value object to web service parameters, or to a relational database? Are you going to serialize it? (Some of that stuff need a default constructor). Can you tell a little bit more about the object?

Raymond Roestenburg
A: 

I would avoid constructors with large numbers of arguments. Classes with lots of arguments in constructors can be unwieldy. Imagine if you have an inheritance heirarchy with subclassses, each with many arguments in their constructors. How much work would if be if need to change the arguments of somes of the top level classes.

I would passing an interface to your contructor which you could change without breaking code, or use the Javabeans approach and have a no arg constructor.

BeWarned
A: 

Can your fields be combined into an intermediate object? For example, if you are passing in 10 fields that describe a person then create a PersonInfo object to pass that data along. I personally prefer passing in all required fields when instantiating the object. That way you don't end up with a half-baked object that will inevitably be used and abused.

cagreen
+1  A: 

Are there variations of the class that might take fewer arguments, or is there just one and it has ten properties?

Is the object meant to be immutable?

Personally, I don't see anything wrong with large constructors, especially if there's only one constructor, and all the properties are final too.

Milan Ramaiya
If all 10 args are different types, it might be ok, but if they are all strings or ints, that's just tempting fate.
Peter Recore
+4  A: 

This two patterns are useful to think about this kind of scenario:

JuanZe
Use a parameter object.The parameter object can check it's own parameters.Don't use "creation methods". they smell.
Tim Williscroft
+2  A: 

If all parameters are in fact mandatory, then I see no reason why not to use a constructor. However, if that's not the case, then using a builder seems like the best approach.
Relying only on setters is in my opinion the worst solutions since there's nothing to enforce that all mandatory properties are set. Of course if you're using Spring Framework's bean wiring or other similar solution, then Java beans are perfectly fine as you can check after the initialization that everything has been set.

Carlos
+2  A: 

I would implement the builder pattern like this:

package so1632058;

public class MyClass {
  private final String param1;
  private final String param2;

  MyClass(Builder builder) {
    this.param1 = builder.param1;
    this.param2 = builder.param2;
  }

  public String getParam1() {
    return param1;
  }

  public String getParam2() {
    return param2;
  }

  @SuppressWarnings("hiding")
  public static final class Builder {
    String param1;
    String param2;

    public Builder param1(String param1) {
      this.param1 = param1;
      return this;
    }

    public Builder param2(String param2) {
      this.param2 = param2;
      return this;
    }

    public MyClass toMyClass() {
      return new MyClass(this);
    }
  }
}

And then have the following code to use it:

package so1632058;

public class Main {

  public static void main(String[] args) {
    MyClass.Builder builder = new MyClass.Builder();
    builder.param1("p1").param2("p2");
    MyClass instance = builder.toMyClass();
    instance.toString();
  }

}

Some notes:

  • There are no methods with many parameters.
  • The additional checking can be done in the MyClass constructor.
  • I made the constructor's visibility package-wide to avoid the "synthetic-access" warning.
  • Same for the builder's instance fields.
  • The only way to construct an instance of MyClass is via the Builder.
Roland Illig
+3  A: 

Ten arguments for a constructor is a lot. I would seriously think about if they are all required and some of them wouldn't make sense to combine into logical objects. If there truly are ten distinct required pieces of data, then the constructor should contain ten fields.

Erick Robertson
+1  A: 

It is a design question no doubt. You have to weigh readability with ease. A ten arg constructor is easier, but may or may not be more readable/maintainable. It also has less method calls to put on and off the call stack. Setting ten distinct values through setters is a bit more readable and explicit. It is not necessarily "easier" (although you could argue both ways), and adds more method calls to put on and off the call stack.

But here are some more points to think about. Even with a ten argument constructor, you could have a programmer choose to pass in a null, a blank, a false, or a zero (depending on object or primitive), which may or may not be what you intended. Your only way to control this would be to potentially throw an exception in the constructor. Is this what you really want to do? Is this the behavior you expect?

Granted, setting each variable separately through setters, you may not be able to know when you are or are not done constructing the object. This is where the Builder pattern discussed above would help. Have it create the object, set the values, and then validate that all are set. If something is missing, because a programmer decided not to pass something in, then you are protected. Your class doesn't have to do more than it is supposed to. (After all, it is important to think about who might be using your class some day. They may not understand your intent, despite all the great Javadoc in the world.)

Lastly, I'd ask if there is anything that you need to default? Because if certain things can default then you can either set them to default values at the class level, or set them to default in the constructor (depending on your programming ideals and which you feel is more specific and assists with the behavior of your object). Then you could potentially "preset" certain fields and only need to override them through setters manually or through setters through a Builder.

Again, you have to decide these things yourself. But probably most important out of all this is the consideration for readability over efficiency to make the code more maintainable, and creating API's and behaviors that programmers that come after you won't be able to abuse. Anticipate abuse protection in your design, whatever you use.

Chris Aldrich
A: 

IMO the constructors don't form a good API while creating objects, especially when the number of arguments are large and they are of the same type.

new Person(String, String, String, String); // what the?? this is what may 
                                            // show up in IDEs

where it actually means Person(firstname, lastname, screenname, password, (just for example's sake)

As cletus mentions, the Builder pattern with chaining is good. Another advantage with a builder is that, if the objects are immutable, the builder can return the same object (in this case you can have a package private constructor with 15 args that only the builder knows about). Builders can also return any subtype of the objects that they build

Another approach you could take is consider using an internal DSL. But this makes sense only if you are building objects like configurations, queries, etc. See if having an internal DSL makes sense in your case.

We had a similar problem in our project. We had to fetch certain values from a home gateway (our product). It supported a request-response, query based XML protocol over http. But creating of Request object for sending in the UI was tedious, with setting up Request obejcts with appropriate parameters and filters, etc.

Initially our request object looked like this:

Request r = new Request("SET");
r.setAction("add"); // modify, add, delete
r.setModuleName("NPWIFI"):
r.addParameter(new Param("wifiAclMac", "aa:bb:cc:dd:ee:ff"));
r.addParameter(new Param("wifiCommit", "commit"));
r.setActionParam("wifiAclRowStatus")
r.addFilter(new Filter(Type.EQUALS, "wifiInterface", "wl0"));
r.addFilter(new Filter(Type.EQUALS, "wifiAclMac", "yourgateway"));

Resonse r = gwSession.sendRequest(r);

So we changed it into an internal DSL that had an SQL like feel but only programmatic

Query q = DQL.add("wifiAclMac", "wifiCommit").into("NPWIFI").values
            ("aa:bb:cc:dd:ee:ff", "commit").withAp("wifiAclRowStatus")
            .where("wifiInterface").is("wl0")
            .and("wifiAclMac").is("aa:bb:cc:dd:ee:ff").end();

The DQL "query builder" object did all the building with validation and also proved to be a lot convenient to use.

Builders and DSLs are a elegant and powerful means of creating and building up objects, but see what makes sense in your case.

naikus
Your example is actually softly typed. A Password for instance could have it's own class, together with encrypting / decrypting logic. A Person would have a Name and Address, and an Account would point to a Person. There are good use cases for keeping things simple and flat, but if you do that you do have loads of similar arguments in your constructor. I prefer to have my abstractions ready made in the data model, which lets the compiler sort it out :)
extraneon
+6  A: 

In his book Code Complete, Steve McConnell argues that no procedure should ever have more than a maximum of 6, maybe 7 arguments. Most of these statements are not just his opinion but backed by studies, e.g. of error rates related to code structure.

Clean Code by Robert Martin goes even further: He recommends 1 or 2 arguments, while 3 is already considered a "code smell". Personally, I think Clean Code is a bit extreme in places, but on the whole it makes some good arguments.

"A whole bunch of parameters" (however many that may be) bespeaks a "kitchen sink" design with lots of afterthoughts and little structure. It also makes maintenance more difficult and error prone; at the very least, it makes code hard to read.

All this makes for good reasons to think about cutting down on the number of parameters. Other answers have offered some practical suggestions.

Carl Smotricz
This is true:) I get suspicious when someone mentions a 10-arg constructor to me. But the trouble is not the constructor, but the necessity of 10 distinct objects to form a valid class. It suggests concepts are getting mingled somehow.
extraneon
@extraneon: While I advocate doing things cleanly, I can also see the other side: A few years ago at work, a colleague designed a class with a boatload of attributes; those attributes were really all part of the object, and values needed to be specified for all of them. The object wouldn't make sense without the attributes. He ended up building a 1500 line constructor with about 30 arguments. I was horrified, but at the time I wasn't able to provide convincing arguments for doing it otherwise - that code has been in production for years! :)
Carl Smotricz
A: 

If all parameters are actually required, and you're not using Spring to instantiate the bean, I would definitely go with a constructor with 10 parameters. It's crystal clear that way that all parameters are, in fact, required.

If you do you Spring (perhaps occasionally) to create the bean, or you don't really like having a lot of temporary variables in the method which constructs that bean, you can also use getters and setters. If you also implement a validate method to check if the object is correct you should not have problems.

The validate method works best however if you use it consistently; perhaps by having a Validatable interface. It fits very well in a workflow if it's part of the programming style. If there are only one or two classes in which this paradigm is used it's probably not worth it. You'll forget to call validate() anyhow someday, somewhere.

If you're not using a validator approach and dislike the constructor (though I wouldn't know why, that's what it's for) you can always fall back to the Builders mentioned by the others.

For what it's worth; if you have 10 required arguments, and you can not group them logically, you may just have mixed to many different concepts in one class; in that case it might be even better to take a good, hard look at your design and refactor a bit.

extraneon
+1  A: 

This is pretty hard to answer in the abstract. What really needs to be done is to look at those ten parameters and see what they are. I see these as the key questions:

  • Can some of them be combined into higher level "value" objects? For example, variables X and Y can be combined into Point. We had lots of situations like this within a cargo routing program where all fields were modeled as primitive strings. Introducing a few higher level concepts really helped make it readable.
  • Can some of the parameters be "defaulted" to certain values?
  • Are they really all independent, orthogonal concepts? I've worked on lots of systems and never seen this to be true. If not, there is some thinking to do about these.
ndp