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
Source: Tips4all, CCNA FINAL EXAM
The better approach (imho) is to use some kind of builder:
ReplyDeleteMyClass 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.
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 ?
ReplyDeleteAre 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.)
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.
ReplyDeleteThe 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.
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.
ReplyDeleteClean 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.
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:
ReplyDeleteWidget widge = new Widget.Builder(). manufacturer("333").serialNumber("54321").build();
This two patterns are useful to think about this kind of scenario:
ReplyDeleteIntroduce Parameter Object from Fowler's "Refactoring".
Replace Constructors with Creation Methods, from Joshua Kerievsky's book, "Refactoring to Patterns".
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.
ReplyDeleteIMHO, you should pass everything that is needed for an object to be valid according to your business logic in the constructor.
ReplyDeleteIf the argument list is lengthy, you could create an object that contains the arguments and pass that.
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.
ReplyDeleteRelying 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.
I would implement the builder pattern like this:
ReplyDeletepackage 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.
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?
ReplyDeleteAre there variations of the class that might take fewer arguments, or is there just one and it has ten properties?
ReplyDeleteIs 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.
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.
ReplyDeleteBut 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.
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.
ReplyDeletenew 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.
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:
ReplyDeleteCan 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.
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.
ReplyDeleteI 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.
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.
ReplyDeleteIf 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.
ReplyDeleteIf 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.