JavaDeveloper
JavaDeveloper

Reputation: 5660

How to design constructors?

Lets assume a class Foo with 2 instance variables, int x and int y. The use case demands I can construct the class with either none, single or all params.

input X    input Y

  0         0           no constuctor

  0          1          public Foo(int x);

  1          0          public Foo(int y);

  1          1          public Foo(int x, int y);

Now whats the convention/best practices in this case. Do I need to add all the permutations of constructors ? If yes, the it would result in exponential growth. If No, then whats solution ?

Upvotes: 0

Views: 251

Answers (5)

andy256
andy256

Reputation: 2881

As @Platinum Azure says, it depends. Since you haven't said that there is a dominant use-case ...

Remember that as a class designer, we are designing for our client programmer. How clear will their code be to a third person? How would they know the meaning of

Foo foo = new Foo();
Foo foo2 = new Foo(2);
Foo foo23 = new Foo(2,3);

And what style of code you want them to follow? The "everythingononeline" style:

Foo foo = new Foo().setNumHeads(2).setNumLegs(3);

Which, when there are many things to set, can be adapted to

Foo foo = new Foo()
    .setNumHeads(2)
    .setNumLegs(3);

Or the traditional OO style:

Foo foo = new Foo();
foo.setNumHeads(2)
foo.setNumLegs(3);

Then, how much work do you need to do now? Do you need a builder, or is Foo simple enough to do without (as in the above examples)?

Another way is to supply defaults:

Foo foo = new Foo(Foo.DefaultNumHeads, Foo.DefaultNumLegs, Foo.DefaultNumTails);

which works well with a few arguments, but can be error prone when there are many if the defaults are defined as ints. So you can apply a variant of the named argument idiom. This is where some suggest a builder, as in Named Parameter idiom in Java, or

Foo foo = new Foo(Foo.DefaultNumHeads, Foo.DefaultNumLegs, Foo.DefaultNumTails);

where Foo.DefaultNumHeads etc are defined as final objects. By the time you get to

Foo foo = new Foo(Foo.NumHeads(2), Foo.NumLegs(3), Foo.NumTails(5));

you are writing a builder inside of Foo.

And what style of constructor is common in the ecosystem Foo lives in? If other classes already have similar arguments, then maybe Foo should too.

The point about combinatorial explosion depends on what change can be expected to Foo. We get a combinatorial explosion if Foo is a not-well-thought-through abstraction and we go back and change Foo again and again in violation of the Open/closed principle. Don't do it.

So, it depends.

Upvotes: 1

Rupesh
Rupesh

Reputation: 2667

Have a constructor that accepts list/array of object. Iterate over it in your constructor and set the values according to your need. I am assuming that you have setter for all the fields available in your class.

Pros: you need not write different constructors

Cons: you need to know and understand that your index or position in list/array decides which field to set.

Foo (Object[] args){
this.setX(args[0]); // have null check accordingly
this.setY(args[1]);    
}

Upvotes: 0

Cameron Skinner
Cameron Skinner

Reputation: 54286

A common approach for this kind of case is to use the builder pattern.

The idea with the builder is that you have an intermediate object that understands the various required or optional arguments and is used to produce an appropriately constructed object. It means you only need to have the all-args constructor in your actual business object, which can greatly simplify things.

You use the builder by doing something like this:

Foo foo = new FooBuilder().withX(10).withY(25).build();

Project Lombok provides a very helpful @Builder annotation that will take care of all this for you: See http://projectlombok.org/features/experimental/Builder.html for details.

Upvotes: 1

Mike Samuel
Mike Samuel

Reputation: 120496

Your best bet is to use the builder pattern as in

new FooBuilder().build()   // Uses default values for both parameters

new FooBuilder().setX(42).build()  // Uses default value for y

new FooBuilder().setY(-13).build()  // Uses default value for x

new FooBuilder().setX(42).setY(-13).build()  // Supplied values for both

and then your builder might look like

class FooBuilder {
  public Foo build() { return new Foo(...); }
  public FooBuilder setX(int x) { ...; return this; }
  public FooBuilder setY(int y) { ...; return this; }
}

Upvotes: 7

Platinum Azure
Platinum Azure

Reputation: 46183

Short answer: It depends.

Longer answer: Add the ones that correspond with real use cases. If that means one with all parameters, or one with none (which sets defaults for the parameters), great. If there are common use cases for having some parameters default and some not, provide constructors for that.

Note that you cannot have multiple constructors with the same order, number, and types of parameters.

Upvotes: 2

Related Questions