Reputation: 475
I'm new to Java after working for a few years in PHP and I'm trying to get an idea of some of the best practices and norms when working with Java.
I'm wondering if it makes sense to make a whole class to act as a custom type just to enforce a limit on the range of an integer argument?
For example:
class Person() {
private int age;
public Person(int age) {
if(age < 0) {
throw new IllegalArgumentException();
}
this.age = age;
}
public int getAge() {
return age;
}
}
Or:
class Person() {
private Age age;
public Person(Age age) {
this.age = age;
}
public int getAge() {
return age.toInt();
}
}
class Age() {
private int age;
public Age(int age) {
if(age < 0) {
throw new IllegalArgumentException();
}
this.age = age;
}
public toInt() {
return age;
}
}
Also, in the second example I'm converting the Age object to an int in Person.getAge(), which is convenient but somehow feels wrong since Person's constructor takes an Age object. Any thoughts?
Thanks!
Upvotes: 1
Views: 604
Reputation: 1119
The first one would get my vote too.
I'm not sure if it is the example here or not, but I wouldn't think setting a age out of range would be a programing error. Throwing IllegalArgumentException makes more sense when a programming error has occurred. Null or a negative age might make my list of conditions to throw an exception.
Based on the post's title, it might be good to consider if the same should be done for an upper limit. I would argue that (using the age example), depending on what is setting the age, that an upper limit might be better enforced by an object validator. The validation should be applied at some point when feedback to the object user can be captured for a reaction (e.g. before persistence) as this wouldn't necessarily be a programming error, but a situation that the user should address first or be notified of.
Upvotes: 0
Reputation: 309008
I think it's about enforcing limits, encapsulating behavior, etc. That's what object-oriented languages are all about. It's getting used to thinking about the world less in terms of primitives like doubles, ints, and strings and more in terms of objects that represent better abstractions that encapsulate and hide details from clients.
I would go for the first example as well. I'd also recommend NOT having an "age" data member, since it changes every year. Better to have a birthDate and calculate current age from it.
Upvotes: 4
Reputation: 61536
I prefer the second version with the Age class as it lets you more easily test the "age" functionality without needing a Peron instance. Also you are able to use the Age in something else, such as a Warrenty.
Having an int for the Age is not a great idea generally - since things gain more age over time. A "creation date" is more general purpose (inside the Age class, just use a Date instead of the int).
Also, you will save yourself a lot of time if you do "throw new IllegalArgumentException("age cannot be < 0, was: " + age);" so that you can know what the offending value was (makes it easier to track bad data down when you know what the value is).
Upvotes: 1
Reputation: 1481
I would also use the first version unless your Age notion is wide spread in your project, in which case you should go the 2nd way around and also be consistent and return an Age object in getAge. Age should have an easy accessor to get the encapsuled int.
Upvotes: 1
Reputation: 8397
I think your second example is a bit overkill for this case, as you only have a single integer with a single constraint on it, and Age
does not have any behavior associated with it. If any of these were different, for instance, if you had more data with interrelated constraints, it might make sense to separate those into their own class. So your second design isn't really wrong, just unsuitable for this toy example.
For your second example, I would probably have the Person.getAge
method return an Age
object instead of an int
. Again, with this toy example it's harder to see a difference, but returning an int
feels like a bit too much coupling between Person
and Age
.
Upvotes: 4
Reputation: 2993
I think first example is ok. Also you should describe thrown exception in javadoc, so user of your class would know about this behavior feature.
Upvotes: 1