Design Question/Constructor Exceptions?

R

Rhino

I was trying to solve a simple problem yesterday when I found myself
wondering about the best design approach for the problem.

I was trying to convert an arbitrary number of seconds into a number of
hours, minutes and seconds. For example, given 3661 seconds, I wanted to
determine that this was 1 hour, 1 minute, and 1 second. Initially, I wrote
this as a static method and returned an int array containing the number of
hours, the number of minutes, and the number of seconds, in that order.

I wasn't wild about passing back an array where the program using it would
just have to know that result was in that order so I thought it might be
better to make a class called HoursMinutesSeconds where the constructor was
HoursMinutesSeconds(int inputSeconds) which calculated the number of hours,
minutes, and seconds for a given value of inputSeconds, then stored those
values in class variables which were made accessible to the outside world
via getters. Then, the user could do this and not have to worry about the
sequence of the values:

HoursMinutesSeconds hms = new HoursMinutesSeconds(3661);
int hours = hsm.getHours();
int minutes = hsm.getMinutes();
int seconds = hsm.getSeconds();

Would you agree that this is a better approach or should I be using a method
instead of a class for this job?

Also, this raised a very interesting followup question. In the real world,
negative seconds don't make a whole lot of sense so it occurred to me that I
might want to prevent any instantation of my HoursMinutesSeconds class if
the input value was a negative number.

Is it reasonable to put conditions like this on a class like
HoursMinutesSeconds? If so, how should I accomplish this?

My first thought was to inspect the value of 'inputSeconds' in the
constructor for the class and if the value was negative, thrown an Exception
of some kind, maybe a 'homegrown' one like
'HoursMinutesSecondsInstantiationException'. However, I don't recall ever
seeing a constructor throw an exception and I vaguely recall reading a
newsgroup post several years back where someone stated categorically that
constructors should never throw exceptions. I may be misremembering that
post though.

If it is reasonable to prevent a constructor from instantiating itself under
certain conditions, what is the best way to accomplish that? I'm inclined to
think that the work should happen outside the class, not in its constructor.
For example, given a method that was supposed to return a number of hours,
minutes, and seconds given a number of seconds via the HoursMinutesSeconds
class, I suspect the best way to prevent instantion when inputSeconds is
negative is something like this:

private void convert() {

int inputSeconds = getSeconds();
if (inputSeconds < 0) {
System.err.println("Number of seconds is negative.");
System.exit(16);
} else {
HoursMinutesSeconds hms = new HoursMinutesSeconds(inputSeconds);
System.out.println("Hours = " + hms.getHours());
System.out.println("Minutes = " + hms.getMinutes());
System.out.println("Seconds = " + hms.getSeconds());
}

So, which is my best approach: prevent instantiation outside the object
itself if the resulting object wouldn't make sense or let the object itself
realize that it wouldn't make sense and abort its instantiation within the
constructor? Or is the whole question nonsense because I shouldn't be using
a class for this purpose in the first place?

--
Rhino
---
rhino1 AT sympatico DOT ca
"There are two ways of constructing a software design. One way is to make it
so simple that there are obviously no deficiencies. And the other way is to
make it so complicated that there are no obvious deficiencies." - C.A.R.
Hoare
 
R

Rhino

I think I just answered my second question - more or less.

It suddenly occurred to me that lots of the Java classes must have the same
issue: what happens if a constructor's input value would not contribute to
creating a valid object? I did a little experiment: I put this line of code
in my program:

JTextField temp = new JTextField("", -5);

This line would create a text field that occupied a negative number of
columns, which doesn't make sense. When I executed the code, I got an
IllegalArgumentException with the message "columns less than zero".

Assuming that the code in JTextField is high-quality code, then it is
apparently good design for the object itself to realize that it can't be
created properly if its inputs aren't valid. The class can handle that
situation by throwing an exception.

Also, I had a look at the source for JTextField just now and it is actually
throwing the exception in
a constructor. Therefore:
a) I am misremembering the post I read which said a constructor should never
throw an exception (very likely)
b) I am remembering the post correctly but the poster was wrong
c) JTextField is not written well and shouldn't be doing what it is doing

Can anyone confirm that it is perfectly reasonable to throw an exception in
a constructor? Then I can stop worrying about this issue.

Rhino
 
C

Chris Smith

Rhino said:
HoursMinutesSeconds hms = new HoursMinutesSeconds(3661);
int hours = hsm.getHours();
int minutes = hsm.getMinutes();
int seconds = hsm.getSeconds();

Would you agree that this is a better approach or should I be using a method
instead of a class for this job?

The above is perfectly fine.
Is it reasonable to put conditions like this on a class like
HoursMinutesSeconds? If so, how should I accomplish this?

Yes, it's definitely reasonable.
My first thought was to inspect the value of 'inputSeconds' in the
constructor for the class and if the value was negative, thrown an Exception
of some kind, maybe a 'homegrown' one like
'HoursMinutesSecondsInstantiationException'.

I'd seriously suggest a java.lang.IllegalArgumentException instead.
That exception class is there specifically for verifying preconditions
on data passed as arguments. If you do declare your own exception type,
then you may consider having it extend IllegalArgumentException.
However, I don't recall ever
seeing a constructor throw an exception and I vaguely recall reading a
newsgroup post several years back where someone stated categorically that
constructors should never throw exceptions. I may be misremembering that
post though.

There is no problem with throwing an exception from a constructor in
Java. There are problems with throwing exceptions from constructors in
some other languages, such as C++; however, it's not a good idea to
carry C++ language limitations over into Java design techniques.

If you choose to throw an exception from your constructor, you are in
good company, along with the likes of:

- Most implementations of InputStream, OutputStream, Reader, or Writer
in the Java standard API.

- The java.lang.ClassLoader class.

- java.net.ServerSocket

And plenty of others.

The only potential problem you might see is a bit of awkwardness if a
subclass wishes to handle an exception from its superclass constructor,
which is not possible and makes little sense anyway.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
A

Andrea Desole

Rhino said:
Would you agree that this is a better approach or should I be using a method
instead of a class for this job?

I would agree, although in general it also depends on how you use the code
Is it reasonable to put conditions like this on a class like
HoursMinutesSeconds? If so, how should I accomplish this?

unsigned long? :)
So, which is my best approach: prevent instantiation outside the object
itself if the resulting object wouldn't make sense or let the object itself
realize that it wouldn't make sense and abort its instantiation within the
constructor? Or is the whole question nonsense because I shouldn't be using
a class for this purpose in the first place?
the check should definitely be in the class, either in the constructor
or in a getter or in a setter. In any case you shouldn't let the class
silently accept a wrong value. In other words, the condition seconds > 0
is an invariant for the class.
I know that someone doesn't want exceptions in the constructors, but
personally I see no problems: a constructor is a method and can throw
exceptions. Also, putting an exception in the constructor gives you the
possibility to know with more precision where the problem is.
 
J

jared_macdonald

HoursMinutesSeconds hms = new HoursMinutesSeconds(3661);
int hours = hsm.getHours();
int minutes = hsm.getMinutes();
int seconds = hsm.getSeconds();

Would you agree that this is a better approach or should I be using a method
instead of a class for this job?

You'll get lots of replies to this message, and mine will be but one.
Anyway:

You know you have a structure (a thing that contains hours, minutes,
and seconds), therefore you must have a class (or resort to the array
of ints approach, which as you determined is not a good solution). The
job of a constructor is to prepare a class to be ready for use, or put
into a valid state; the way to put HoursMinutesSeconds into a valid
state is to provide it with some seconds; therefore, a constructor is
appropriate.
However, I don't recall ever
seeing a constructor throw an exception and I vaguely recall reading a
newsgroup post several years back where someone stated categorically that
constructors should never throw exceptions. I may be misremembering that
post though.

Constructors certainly may throw exceptions, and it's the right thing
to do in this case. Constructors are no different from methods in terms
of throwing checked or unchecked exceptions. In your case, there is an
exception made for the situation you describe of negative seconds:
IllegalArgumentException.
private void convert() {

int inputSeconds = getSeconds();
if (inputSeconds < 0) {
System.err.println("Number of seconds is negative.");
System.exit(16);

I think you're describing two types of checks. The first is the
internal check (the one for you, the programmer, or for other
programmers) that's part of your HoursMinutesSeconds API. This comes in
the form of throwing an IllegalArgumentException:

public HoursMinutesSeconds(int seconds)
{
if (seconds < 0)
throw new IllegalArgumentException("seconds must >= 0: " +
seconds);
...
}

The second check that I'm inferring is for some user of your program,
who's entering a number in an application or on the command line, say.
Here, you don't want to use exceptions. You want to check the validity
of the user's input as part of the standard path of your program:

if (typedSeconds < 0)
{
System.err.println("Seconds cannot be negative."); // or dialog box,
etc.
return;
}
else
{
HoursMinutesSeconds hms = new HoursMinutesSeconds(typedSeconds);
...
}

There are lots of other issues to explore with this little example --
clearly-named factory methods rather than a constructor, when to use
checked versus unchecked exceptions -- but these have all been covered
time and again in other discussions, and this should be enough to get
you going.
 
R

Rhino

There are lots of other issues to explore with this little example --
clearly-named factory methods rather than a constructor, when to use
checked versus unchecked exceptions -- but these have all been covered
time and again in other discussions, and this should be enough to get
you going.
Darn! Just when I thought I had a good approach figured out, it turns out
there are all sorts of variations to consider, each with their own pros and
cons.... ;-)

I guess I'd better start with factory methods....

Rhino
 
D

Dimitri Maziuk

Rhino sez:
.... I vaguely recall reading a
newsgroup post several years back where someone stated categorically that
constructors should never throw exceptions. I may be misremembering that
post though.

It probably was a C++ newsgroup. There are reasons for never
throwing an exception from constructor in C++ but they don't
(necessarily) apply to Java.

Dima
 
C

Carl

Rhino said:
Darn! Just when I thought I had a good approach figured out, it turns out
there are all sorts of variations to consider, each with their own pros and
cons.... ;-)

I guess I'd better start with factory methods....

Rhino

Based on your earlier post I think I see what you are getting at with
this. My suggestion would be to take a look at java.text.DateFormat,
I've found this class easy to use, and it seem to implement the type of
features you are interested in (including support for locale). This may
be a good class to model your own after.

Carl.
 
J

Jacob

Rhino said:
HoursMinutesSeconds hms = new HoursMinutesSeconds(3661);
int hours = hsm.getHours();
int minutes = hsm.getMinutes();
int seconds = hsm.getSeconds();

This class is a rather artificial bundle of three integers, and
I'd be tempted to suggest that the int[3] approach is as good.

It's a bit like the ever arising issue in geometry: Should there
be a Point3D of x,y,z or just a double[3]. We're almost always
better off with the pragmatic latter solution.

An alternative in your case could be:

class TimeUtil {
static long getWholeNumberOfHours (long seconds);
static long getWholeNumberOfMinutes (long seconds);
static long getWholeNumberOfSeconds (long seconds);
}

Also, this raised a very interesting followup question. In the real world,
negative seconds don't make a whole lot of sense so it occurred to me that I
might want to prevent any instantation of my HoursMinutesSeconds class if
the input value was a negative number.

What you lack is a type of positive integers in order to enforce
correct usage. You could create such a type, but that is probably
an overkill.

An alternative is simply to broaden the definition of the input:
A negative number of seconds is a good as a positive one, but
pointing in "the other" direction. So 2110 and -2110 should give
the same result as it is equally many hours:minutes:seconds from
the starting point.

If you stick to your original approach, I'd consider a simple
assert instead of an exception.
 
O

opalpa

Rhino, throw IllegalArgumentException instead of a home grown
exception. Those coming from c++/c/fortran frequently are ready to
create more infrasttruce then prudent; they are having a hard time
beleving that RuntimeException, IllegalArgumentException,
UnsupportedOperationException, IllegalStateException etc will be well
divided for them. Your situation, an illegal argument is covered. I
also utilize IllegalArgumentException instead of System.exit inside of
main when checking arguments to main(String args[]). The exit code to
the shell is 1.

Someone here recommended unsigned long which is not available in java.
Even if the language had this representation I'd recommend against.

Accessors for seperate parts are more explicit and my preference over
an array of three values.
 
J

Joona I Palaste

Rhino said:
Also, this raised a very interesting followup question. In the real world,
negative seconds don't make a whole lot of sense so it occurred to me that I
might want to prevent any instantation of my HoursMinutesSeconds class if
the input value was a negative number.

No one has addressed this before, but I think you don't need to place
such arbitrary restrictions on time values. Negative time does make
sense in some situations, for example when you want to substract one
time value from another.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top