views:

712

answers:

9

Following are the two approaches:

  • constructor with all the class properties

Pros: I have to put an exact number of types of parameters so if I make an error the compiler warns me (by the way, is there a way to prevent the problem of having erroneously switched two Integer on the parameter list?)

Cons: if I have lots of properties the instantiation line can become really long and it could span over two or more lines

  • setters and the default empty constructor

Pros: I can clearly see what I'm setting, so if I'm doing something wrong I can pinpoint it as soon as I'm typing it (I can't make the previuos error of switching two variables of the same type)

Cons: the instantiation of an object with lots of properties could take several lines (don't know if this is really a con) and if I forget to set a property the compiler doesn't say anything.

What will you do and why? Do you know of any light pattern (consider that it should be used everytime an object wth 7+ properties is instantiated) to suggest? I'm asking this because I tend to dislike large constructors where I can't figure out fast where is the variable I'm looking for, on the other hand I find the "set all properties" vulnerable to missing some of the properties.

Feel free to argument my assumptions in pros and cons as they are only mine thoughts :)

+18  A: 

You've missed the biggest pro of having a constructor with loads of parameters: it lets you create immutable types.

The normal way of creating immutable types without huge constructor nastiness is to have a helper type - a builder which maintains the values you'll want in your final object, then builds the immutable object when you're ready.

Jon Skeet
Isn't a builder the better strategy anyway?
Uri
Additionally, if the state of your object can't run without the properties being set, then force the user to set them by going through the constructor.
Joshua Belden
@Uri: The builder pattern is over the top IMO if you've only got a few properties.
Jon Skeet
@Jon Skeet: Thanks for pointing out the existance of immutable types, I did not know about them.
Alberto Zaccagni
@Jon Skeet is right on. I actually consider setters outside of builders a bad smell these days. Immutable classes are easier to reason about in general, and *much* easier to reason about in a concurrent setting.The one downside to the Builder pattern (which it shares with using setters) is that it doesn't work as well for properties that lack a default value. You can check that all required properties have been set when build() is invoked, but then you're talking about a runtime check instead of a compile time check. It's too bad Java doesn't have keyword arguments.
Laurence Gonsalves
+2  A: 

Recent academic research (CMU and Microsoft) on API usability suggests that default constructors with setters would be the way to go in terms of usability. This is from "Usability Implications of Requiring Parameters in Objects' Constructors" by Jeff Stylos and Steven Clarke and was presented at the International Conference on Software Engineering:

Abstract: The usability of APIs is increasingly important to programmer productivity. Based on experience with usability studies of specific APIs, techniques were explored for studying the usability of design choices common to many APIs. A comparative study was performed to assess how professional programmers use APIs with required parameters in objects' constructors as opposed to parameterless "default" constructors. It was hypothesized that required parameters would create more usable and self-documenting APIs by guiding programmers toward the correct use of objects and preventing errors. However, in the study, it was found that, contrary to expectations, programmers strongly preferred and were more effective with APIs that did not require constructor parameters. Participants' behavior was analyzed using the cognitive dimensions framework, and revealing that required constructor parameters interfere with common learning strategies, causing undesirable premature commitment.

Uri
+11  A: 

You might look at the Builder pattern advocated by Joshua Bloch, and described in Effective Java. There's a presentation with the main points at http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-2689.pdf; no doubt you could dig up a better reference.

Basically, you have another class, probably an inner class, which provides methods named after the properties being set, and which return the original builder so you can chain calls. It makes for quite a readable chunk of code.

For example, let's suppose I have a simple Message with a few properties. The client code constructing this could use a builder to prepare a Message as follows:

Message message = new Message.Builder()
    .sender( new User( ... ) )
    .recipient( new User( ... ) )
    .subject( "Hello, world!" )
    .text( messageText )
    .build();

A fragment of Message.Builder might look similar to the following:

public class Builder {

    private User sender = null;
    // Other properties

    public Builder sender( User sender ) {
        this.sender = sender;
        return this;
    }
    // Methods for other properties

    public Message build() {
        Message message = new Message();
        message.setSender( sender );
        // Set the other properties
        return message;
    }

}
Rob
Thanks for the explaining code
Alberto Zaccagni
Builders are really nice if you have a complex object. I can strongly recommend it.
Thorbjørn Ravn Andersen
and the book! It is really a must-read for Java programmers - it is available on O'Reilly Safari which is where I read it (we have the smallest subscription)
Thorbjørn Ravn Andersen
You can also have the builder object implement several interfaces, each with one method returning the interface containing the next method, so you have to call all the builder methods in a predefined sequence and you can only call build() once all setters have been called...
pgras
Interesting thought, that. Personally, I try to avoid having too many mandatory properties in a given object, and I tend to use the Builder pattern in such a way that I'm using the property-named methods to set the optional properties, but it could certainly be an interesting option. Of course, you can just throw an IllegalStateException if the client hasn't set up all the required properties.
Rob
A: 

There are other aspects as well. If you want to be able to certain things with your class at design time rather than just at runtime, for example adding your class as an object in the Object Palette (this is Java using Netbeans) you need to provide a no-argument constructor in order to be able to do so.

JRL
This is an important consideration -- ORM tools (e.g. Hibernate although I'm not sure if the latest version still does) tend to require accessible setters and a default constructor.
hromanko
+2  A: 

You mention it in your post, but I think this is an important point that deserves more attention: unless every input parameter is a different type, the big problem with huge constructors is that it's very easy to transpose a couple of variables. The compiler is an unreliable safety net -- it will catch some mistakes, but the ones that slip through are going to be much more difficult to identify and debug. Especially because the input list for a huge constructor is quite opaque unless you've got the API open in another window.

Getters and setters are vastly easier to debug, especially if you institute safeguards that throw a runtime exception if the object isn't properly populated. And I'm a huge fan of "easy to debug."

Prior to this thread I'd never heard of the Builder pattern Rob mentions. Never used it myself (obviously), but it's damned intriguing.

BlairHippo
A: 

There are other strategies here, too. Before trying to figure out how to deal with lots of parameters, I think it is important to re-visit your design and look at whether your class is doing too much. See if you can group some of the parameters together into a new class, and move some behavior into that class.

Adam Jaskiewicz
A: 

setters and the default empty constructor

JRL obliquely touched on it, but one reason to consider using setters is to have the object conform to the JavaBean specification. This makes instances amenable to editing via introspection tools and persistence using certain serialization techniques.

McDowell
+1  A: 

I prefer taking constructor arguments, for the aforementioned immutability reasons. If that gives you a constructor that takes lots of arguments (say more than four or so), that's a code smell to me: some of those arguments should be bundled together into their own types.

For example, if you have something like this:

class Contact
{
    public Contact(string firstName, string lastName, string phoneNumber,
        string street, string city, string state, int zipCode) { ... }
}

I'd refactor it to:

class Contact
{
    public Contact(Person person, PhoneNumber number, Address address) { ... }
}

class Person
{
    public Person(string firstName, string lastName) { ... }
}

class PhoneNumber
{
    public PhoneNumber(string digits) { ... }
}

class Address
{
    public Address(string street, string city, string state, int zipCode) { ... }
}

Too-large classes are a really common design problem in OOP codebases.

munificent
how do you deal with zipCode optional?
Sam Saffron
Overload Address's constructor.
munificent
A: 

Who says you can't do both? I'd say mandatory properties go into the constructor, optional ones are handled with setters. BTW, who says you always need one setter per property? If two properties belong together conceptually, why not set them together?

I like the Builder pattern too, but the most important rule is: always use your brain and find the design that best fits the specific problem. There's no one-size-fits-all solution.

Michael Borgwardt