Java Forum / General / August 2007
OO Design: What goes into the constructor?
antonyliu2002@yahoo.com - 09 Aug 2007 15:29 GMT I have always been wondering about this question: How do we decide what should be put into the constructor?
For example, a Project class can have a lot of attributes, such as ProjectID, ProjectName, ProjectManagerID, ProjectDescription, ProjectObjective, ProjectProgramAreaID, ProjectStatus, ProjectFundID, ProjectFundStatus, ProjectSchedule, etc.
Now, when I am writing the Project class, how do I decide which and which of such information go to the constructor(s)? It apparently does not make sense to cram all of them into a single constructor, right? And if we decide to have multiple constructors, what information needs to be selected and put into which constructor?
I am confused, please shed some light on this issue. Thank you.
tzvika.barenholz@gmail.com - 09 Aug 2007 16:36 GMT On Aug 9, 5:29 pm, "antonyliu2...@yahoo.com" <antonyliu2...@yahoo.com> wrote:
> I have always been wondering about this question: How do we decide > what should be put into the constructor? [quoted text clipped - 11 lines] > > I am confused, please shed some light on this issue. Thank you. 1. It's generally a good idea to make objects immutable, and put *everything* in the constructor.
2. If you do find yourself creating multiple constructors, i.e. because some properties are optional - use static factory methods that drain into one private constructor. That way you can give them names that make sense.
3. If you find yourself with an unmanageably large number of parameters in the constructor, it's usually a good sign that you've got an unmanageably large number of properties in the class. take the opportunity to refactor it, break it up, group certain properties into classes of their own, if it makes sense, and so on.
T
Joe Attardi - 09 Aug 2007 16:59 GMT > 1. It's generally a good idea to make objects immutable, and put > *everything* in the constructor. In some cases, sure, but I don't know if I'd call this a general rule. Why do you say objects should generally be immutable?
 Signature Joe Attardi jattardi@gmail.com
tzvika.barenholz@gmail.com - 09 Aug 2007 17:44 GMT > tzvika.barenh...@gmail.com wrote: > > 1. It's generally a good idea to make objects immutable, and put [quoted text clipped - 6 lines] > Joe Attardi > jatta...@gmail.com I merely repeat after Bloch, so I can't take the credit. Immutability makes code easier to debug, because objects don't go changing on you. It also makes objects fit well within frameworks like collections (imagine putting something in a hash set, then changing its internal state which gives it a different hashcode).
Of course not everything can be immutable, because some object do genuinely change state without stopping to be themselves. In other cases the cost of construction and instantiation prohibits it. But I think generally immutable is the way to go :-)
T
kaldrenon - 09 Aug 2007 18:09 GMT On Aug 9, 12:44 pm, "tzvika.barenh...@gmail.com" <tzvika.barenh...@gmail.com> wrote:
> I merely repeat after Bloch, so I can't take the credit. Immutability > makes code easier to debug, because objects don't go changing on you. I know there are cases where this is true. But the way I see it, there's immutable, and there's /immutable/.
There are plenty of instances where one is using a class to represent a collection of information and knows full well that the information is going to receive updates over time. How one goes about making those updates is a matter of style, and good OO with encapsulation suggests that it's better to give the class a behavior than to perform the behavior outside of the class and modify within (e.g. a deposit() method instead of a setBalance() method for a Bank). But if every one of your classes is solely a grouping of data and no behaviors....well at the very least, that's boring. but it doesn't sound all that helpful, either.
So when you say immutability, are you referring to something like not using setters, or something more absolute?
Roedy Green - 09 Aug 2007 19:49 GMT On Thu, 09 Aug 2007 16:44:39 -0000, "tzvika.barenholz@gmail.com" <tzvika.barenholz@gmail.com> wrote, quoted or indirectly quoted someone who said :
>I merely repeat after Bloch, so I can't take the credit. Immutability >makes code easier to debug, because objects don't go changing on you. >It also makes objects fit well within frameworks like collections >(imagine putting something in a hash set, then changing its internal >state which gives it a different hashcode). Immutable objects are much better behaved in multi-thread situations. You can't get into trouble with two threads updating the same thread at once, or one thread seeing an object half way through an update by another. With immutability, thread interaction becomes relatively mindless. With mutability, it becomes a black art.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Thomas Hawtin - 09 Aug 2007 17:19 GMT > 3. If you find yourself with an unmanageably large number of > parameters in the constructor, it's usually a good sign that you've > got an unmanageably large number of properties in the class. take the > opportunity to refactor it, break it up, group certain properties into > classes of their own, if it makes sense, and so on. Before that you might consider using a builder. That way you can build up the object one (labeled) property at a time.
So the original example would become:
Project project = new Project.Builder() .id(id) .name(name) .description(description) .objective(objective) .programAreaID(programAreaID) .status(status) .fundID(fundID) .fundStatus(fundStatus) .schedule(schedule) .create();
Tom Hawtin
antonyliu2002@yahoo.com - 09 Aug 2007 17:30 GMT > tzvika.barenh...@gmail.com wrote: > [quoted text clipped - 22 lines] > > Tom Hawtin Tom, are those dotted identifiers (.id, .name, .description, etc.) setter methods? I am confused by the syntax.
Chris Riesbeck - 09 Aug 2007 19:07 GMT >> tzvika.barenh...@gmail.com wrote: >> [quoted text clipped - 24 lines] > Tom, are those dotted identifiers (.id, .name, .description, etc.) > setter methods? I am confused by the syntax. I think Tom is referring to the Builder pattern
http://en.wikipedia.org/wiki/Builder_pattern
In Project you define an inner class Builder.
.id(), .name() etc are methods of Project.Builder that add labelled data to a Project.Builder instance. They all return "this" so methods can be chained.
.create() is a method that takes that instance data and returns a newly constructed Project.
So the opening line, "new Project.Builder()" creates an empty Project.Builder instance, and the rest of the lines fill it in, and the final line returns the Project.
Wojtek - 09 Aug 2007 19:19 GMT Chris Riesbeck wrote :
>>> tzvika.barenh...@gmail.com wrote: >>> [quoted text clipped - 40 lines] > instance, and the rest of the lines fill it in, and the final line returns > the Project. Just make sure that the create() method does a sanity check to ensure that ALL the required information has been set. So someone does not simply call:
Project project = new Project.Builder().create();
 Signature Wojtek :-)
antonyliu2002@yahoo.com - 09 Aug 2007 17:26 GMT On Aug 9, 11:36 am, "tzvika.barenh...@gmail.com" <tzvika.barenh...@gmail.com> wrote:
> On Aug 9, 5:29 pm, "antonyliu2...@yahoo.com" <antonyliu2...@yahoo.com> > wrote: [quoted text clipped - 30 lines] > > T Yes, I do quite often find myself flooded with a large number of parameters which I attempt to place in the constructor(s). You say that in such cases, we may be able to break it into smaller classes. But does it make sense to break the Project class into multiple smaller classes?
Or is it better to simply put the ProjectID and ProjectName in the constructor, and make all other attributes/properties/fields (such as ProjectDescription, ProjectObjective, ProjectManagerID, ProjectStartDate, ProjectContractorID, ProjectFundSourceID, ProjectActualCost) available in the getter and setter methods?
Rob - 09 Aug 2007 19:34 GMT On Aug 9, 11:29 am, "antonyliu2...@yahoo.com" <antonyliu2...@yahoo.com> wrote:
> I have always been wondering about this question: How do we decide > what should be put into the constructor? [quoted text clipped - 11 lines] > > I am confused, please shed some light on this issue. Thank you. This is a good question.
What I've found works well is the constructor contains the minimal amount to create a valid instance of the object with setters for the other stuff.
If you know how the client code will be used this can be helpful. For a small number of fields where the client has all the information at hand when he creates the object then the constructor can specify everything.
In the project example it is quite possible that at the time the client needs to create a Project he may only have some of the data at hand and will need to get the rest later. So I'd let the client create a valid skeleton instance and add the other stuff as applicable or when he actually has it.
Rob http://cbmc64.blogspot.com
Martin Gregorie - 10 Aug 2007 01:02 GMT > On Aug 9, 11:29 am, "antonyliu2...@yahoo.com" > <antonyliu2...@yahoo.com> wrote: [quoted text clipped - 23 lines] > hand when he creates the object then the constructor can specify > everything. That works for me, but I'd add another rule: don't put anything in the constructor that throws an exception that must be caught outside the new object declaration. Violating this rule tends to cause scoping problems.
 Signature martin@ | Martin Gregorie gregorie. | Essex, UK org |
Lew - 15 Aug 2007 13:01 GMT > That works for me, but I'd add another rule: don't put anything in the > constructor that throws an exception that must be caught outside the new > object declaration. Violating this rule tends to cause scoping problems. I wouldn't add that rule. "Scoping" is totally not involved in exceptions thrown from constructors; I really don't even know what you meant by that.
It's perfectly legitimate to throw an exception from a constructor.
 Signature Lew
Martin Gregorie - 16 Aug 2007 11:59 GMT >> That works for me, but I'd add another rule: don't put anything in the >> constructor that throws an exception that must be caught outside the [quoted text clipped - 4 lines] > exceptions thrown from constructors; I really don't even know what you > meant by that. Maybe its a stylistic error on my part, but I like to put all declarations at the head of a method and to keep try blocks as short as possible. The requirement to wrap a declaration in a try block if it throws exceptions tends to go against this style. In consequence, I move the bits of a constructor that can throw exceptions into a method.
For instance, I have a Java class that's the equivalent of the C getopt() function. I use it in command-line applications to parse the command line, setting flags etc from options and building an array of non-option arguments. The constructor merely captures the args[] array together with the list of valid options and whether they take mandatory or optional values. Parsing the args[] array can throw an exception if it discovers invalid options, so its split out into a separate method. This way I can localize it and the error exit in a small try block. Once that's out of the way I can go on to deal with the lists of options and non-options outside the try block because the methods for that (nextOption() + getValue(), nextArgument()) don't throw exceptions but may well control code that does throw completely unrelated exceptions of their own: to me its cleaner to keep these two types of exceptions in separate try blocks.
> It's perfectly legitimate to throw an exception from a constructor. Sure. I'm just suggesting that there are reasons for not doing so. I'll be interested to hear your opinion of this reasoning.
 Signature martin@ | Martin Gregorie gregorie. | Essex, UK org |
Lew - 16 Aug 2007 12:58 GMT Lew wrote:
>> It's perfectly legitimate to throw an exception from a constructor.
> Sure. I'm just suggesting that there are reasons for not doing so. I'll > be interested to hear your opinion of this reasoning. Your reasoning is quite sound. I'd phrase the principles involved a little differently, but I'd bet I'd come up with the same conclusions.
While I said that constructors can legitimately throw exceptions, I didn't say they should, necessarily. Constructors should throw exceptions only if the in the logic of construction it makes sense. For example, in your GetOpt class I'd consider throwing an exception if args[] were null. That way the client code finds out at the point of error that there was a problem. Throwing an NPE in the process() method (or whatever you call it) is useful, but could happen distant from the point of error, passing null to the constructor.
Then I'd likely reject that idea and treat null args the same as empty args, i.e., no options.
But that's the kind of reasoning that might lead me to throw an exception from a constructor. I admit I would resist it until compelled. What would compel is a condition that must halt the program, like a Data Access Object (DAO) not finding its datastore driver.
As for not processing args in the constructor, that's a different principle for me. Constructors are for construction, period. Processing the args is not part of constructing the object; passing them into an instance variable is. Doing too much in a constructor is a mistake in its own right, and leads to various kinds of trouble.
We converge in that the less one does in a constructor, the less excuse one has to throw exceptions there. With the constructor's purpose clearly understood and constrained, the kinds of things that can throw exceptions in it tend to be excluded.
 Signature Lew
Martin Gregorie - 17 Aug 2007 00:16 GMT > Your reasoning is quite sound. I'd phrase the principles involved a > little differently, but I'd bet I'd come up with the same conclusions. I've come to Java from C with almost no formal background or training in OOD techniques. In C I've developed a personal pseudo-OO style that just growed because it minimizes bad interactions. In it I store variables as control-block structs (very similar to the way that the FILE struct is used) or as globals with their scope limited to the source file if there's never more than one "instance" possible per program. All access to these variables are solely via functions. I also write functions equivalent to constructor and destructor. The major difference is that, because the constructor function can return a status code, I'll often do the equivalent of parsing the option definition string within it. I think its arguable whether that counts as part of initialization: my conclusion has been that in C the constructor is a good place to put it but in Java its often better to split it out.
This seemed like a good place in the thread to check whether this is sensible, so thanks for your feedback.
> While I said that constructors can legitimately throw exceptions, I > didn't say they should, necessarily. I assumed that was what you meant.
> Then I'd likely reject that idea and treat null args the same as empty > args, oi.e., no options. Which is exactly what it does :-)
I also use setters to handle seldom used options rather than multiple constructors. An example would be to turn option case sensitivity on (default is off).
 Signature martin@ | Martin Gregorie gregorie. | Essex, UK org |
Joe Attardi - 16 Aug 2007 15:56 GMT > Sure. I'm just suggesting that there are reasons for not doing so. I'll > be interested to hear your opinion of this reasoning. One particular instance I can think of is to throw an IllegalArgumentException if arguments are passed that just don't make any sense.
 Signature Joe Attardi jattardi@gmail.com
Martin Gregorie - 17 Aug 2007 00:23 GMT >> Sure. I'm just suggesting that there are reasons for not doing so. >> I'll be interested to hear your opinion of this reasoning. > > One particular instance I can think of is to throw an > IllegalArgumentException if arguments are passed that just don't make > any sense. Agreed, though validation generally requires more processing than I'd now do in the constructor. I'm with Lew here. Also, suitable choice of parameters (using booleans in place of ints or strings and/or passing appropriate classes) can hand off quite a bit of simple validation to the compiler.
 Signature martin@ | Martin Gregorie gregorie. | Essex, UK org |
antonyliu2002@yahoo.com - 10 Aug 2007 16:04 GMT > On Aug 9, 11:29 am, "antonyliu2...@yahoo.com" > [quoted text clipped - 33 lines] > > Robhttp://cbmc64.blogspot.com So, the idea is to put those most important features into the constructor in a situation where one is flooded with a large number of attributes.
Roedy Green - 09 Aug 2007 19:46 GMT On Thu, 09 Aug 2007 14:29:13 -0000, "antonyliu2002@yahoo.com" <antonyliu2002@yahoo.com> wrote, quoted or indirectly quoted someone who said :
>Now, when I am writing the Project class, how do I decide which and >which of such information go to the constructor(s)? It apparently >does not make sense to cram all of them into a single constructor, >right? And if we decide to have multiple constructors, what >information needs to be selected and put into which constructor? One goal of a constructor is that when you are done the object should be logically complete containing all mandatory fields. I don't like logically incomplete objects floating around the universe. This is a stylistic goal, not something built in to Java.
When constructors get too hairy, you can create constructors that accept aux aggregate objects as parms. That way you avoid creating objects that need more setter calls before they can be used.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Joshua Cranmer - 10 Aug 2007 23:01 GMT > I have always been wondering about this question: How do we decide > what should be put into the constructor? [quoted text clipped - 11 lines] > > I am confused, please shed some light on this issue. Thank you. I just found Norvig's Java Infrequently Answered Questions page earlier today, and one of them is marginally relevant:
Q: I have a class with six instance variables, each of which could be initialized or not. Should I write 64 constructors? (Relevant parts of the answer) 2. Define setters that can be cascaded because they return this. That is, define a setter for each instance variable, then use them after a call to the default constructor: [ code omitted ] Pro: This is a reasonably simple and efficient approach. A similar idea is discussed by Bjarne Stroustrop on page 156 of The Design and Evolution of C++. Con: You need to write all the little setters, they aren't JavaBean-compliant (since they return this, not void), they don't work if there are interactions between two values.
3. Use the default constructor for an anonymous sub-class with a non-static initializer:
new C() {{ a = 1; c = 3; e = 5; }}
Pro: [...] Con: [...] When I showed this to Guy Steele, he said "heh, heh! That's pretty cute, all right, but I'm not sure I would advocate widespread use..." As usual, Guy is right.
Full text of the question, answer, and whole IAQ is here: http://norvig.com/java-iaq.html
 Signature Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald E. Knuth
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|