Introducing Constraints

But what happens if I try to set age to a negative number or a very large number? Maybe I should introduce some constraints on age.

I’ll start by defining some constants for Person, test-first of course.

images/testconstants.png

I get the red squiggly line under MINIMUM_AGE and MAXIMUM_AGE because they haven’t been defined yet. I’ll let Eclipse autogenerate them in the Person class and then I’ll clean them up so they read

 public​ ​static​ ​final​ ​int​ MINIMUM_AGE = 1;
 public​ ​static​ ​final​ ​int​ MAXIMUM_AGE = 200;

This defines the symbols I’ll use to set the lowest and highest ages I’ll accept to 1 and 200. I’ve never heard of anyone living to 200, and by the time somebody does I’ll be long gone so I won’t need to update the program.

And what I just did right there is to create what might actually become an example of legacy code. It’s like writing banking software (or any software) that assumes the first two digits of every year will be 19. What I’m depending on here is that, when we do get to a point where people are routinely living past the age of 200, anyone who might need to revise my code accordingly will easily find the constant marked MAXIMUM_AGE, and be able to change that to equal 300 or 500 or 5,000, because I have used simple language and intention-revealing names—which we’ll cover in more detail in the next chapter.

Write the Test and Stub Out the Code

Next, I’ll write a test to verify that the age passed in is not too small. Since the data type I’m using to hold the age is an int, which in Java is 32 bits, it can hold numbers from -2,147,483,648 to 2,147,483,647, so I have to guard against negative numbers. I’ll write a test to verify that when I pass in an age that is one less than MINIMUM_AGE it will throw an exception to signal an error. I’ll add the following test method to the PersonTest class.

images/agebelowminimumexceptionclass.png

This is a special annotation that tells JUnit that when setAge() is called with a value of 0 (MINIMUM_AGE – 1) then it should expect a Person isn’t instantiated. Instead an AgeBelowMinimumException is thrown, which is underlined because it doesn’t exist yet. I’ll let Eclipse generate the stub for me:

 public​ ​class​ AgeBelowMinimumException ​extends​ RuntimeException {
 }

Now everything is defined and I can run my tests and I get…

Red bar!

Implement the Behavior

I have a test but I haven’t implemented the new behavior in my code yet, so I’ll do that by updating Person’s constructor:

 public​ Person(String name, ​int​ age) {
 if​ (age < MINIMUM_AGE){
 throw​ ​new​ AgeBelowMinimumException();
  } ​else​ {
 this​.age = age;
  }
 this​.name = name;
 }

This says that when an instance of Person is created and the age passed in is less than MINIMUM_AGE then throw an exception instead of creating a new instance of Person. When a parameter makes no sense, I should probably throw an exception and exit instead of creating an invalid instance of Person that could end up doing who knows what to the system.

Notice that this test tests things in reverse, so to speak.

If I try to create a Person with an age less than MINIMUM_AGE, I want the system to throw an exception. If it doesn’t that’s bad, so my test verifies that the system throws an exception when age is less than MINIMUM_AGE and if it doesn’t throw an exception JUnit will put up the red bar.

Finally, I’ll write a test that expects an exception when an instance of Person is created with an age greater than MAXIMUM_AGE:

images/ageabovemaximumexceptionclass.png

And I’ll let Eclipse stub out the exception:

 public​ ​class​ AgeAboveMaximumException ​extends​ RuntimeException {
 }

In practice, I’d probably use a single exception, AgeOutOfRangeException, instead of AgeBelowMinimumException and AgeAboveMaximumException, but I wanted to demonstrate that a range has two boundaries and I could do something different for each boundary condition.

Once again I can compile. When I run the test I get the red bar again telling me I have to handle this new exception in my code. So I’ll handle it by adding to the constructor of Person:

 if​ (age > MAXIMUM_AGE) {
 throw​ ​new​ AgeAboveMaximumException();
 }