• Post Reply Bookmark Topic Watch Topic
  • New Topic

More on nulls and null return values  RSS feed

 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To staisfy my urge to write more Java code, I am creating an address book application. One of my (admittadely self imposed) requirements for this application is that no public method should accept null parameters, nor return null "Objects", instead opting for zero-length strings and arrays, and the like. However I've run into a problem.

I have an AreaCode class that not only contains the three digit exchange, but also the state, and locality associated with the AreaCode. The class has a private constructor and a static valueOf() method which accepts a String as it argument. The String is validated as a three digit number using a regular expression, and a static Map<String, AreaCode> (which is parsed from a csv file of current North American area code data) is searched. If the map contains the String as a key, that AreaCode is returned. If the map doesn't contain the String, an AreaCode is returned, but the the locality and state fields are initialized to empty Strings.

I also have a TelephoneNumber class that predictably has a member variable which is an AreaCode. The TelephoneNumber class also has a valueOf() method that parses a telephone number String into a TelephoneNumber. The problem is, the telephone number doesn't necessarily have to contain an area code. It could just be a seven digit number or a seven digit number plus an extension of variable length. My porblem is, when the TelephoneNumber doesn't contain an AreaCode, how do I signal this without returning a null value from the getAreaCode() method.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In the previous thread Stan suggested googling "null object pattern" - I second this recommendation. You could have a MissingAreaCode subclass (or anonymous class used for a single missingAreaCode instance) with whatever custom behavior you need for a missing area code.

Note that you may also want to make the root TelephoneNumber interface fairly vauge, as there are a number of different formats used internationally. (nnn)nnn-nnnn is a US telephone number, but many companies should not arbitrarily limit themselves to US customers. Just something to keep in mind as you go...
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Jim. that is exactly what I needed, further it forced me to define another layer of abstraction in the form of an AreaCode interface, and implement that interface in my AreaCodeIpml and NullAreaCode classes.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Another choice is to create a method hasAreaCode() which returns a boolean and then force clients to use that before attempting to get the area code. If they attempt to get an area code for a number that does not have one an exception is thrown, IllegalStateException for example. I personally dislike this option, but I'm throwing it out there none the less.

Also, I'm not sure I would put the state and locality in the AreaCode. Rather, I think I would provide a separate class that could lookup the state (if any) of an AreaCode. An AreaCode, afterall, does not necessarily have a state with numbers outside the U.S.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Looking at our attributes module what we do is we have an areaCode, extension, internationalPrefix, and a number. We have separate classes that deal with looking up the state an area code is in, or which country is associated with a prefix, etc.

EDIT: Furthermore I think it's kind of a good idea that you code with the notion that you shouldn't accept or return null. We've been discussing over the past several weeks what to do about the rampant null values in our attributes module and have yet to come to a satisfactory solution. The "null object pattern" exposes implementation, but tossing around null values does too and leads to rampant NullPointerExceptions and annoying bugs that pop up at runtime. It certainly doesn't seem like an easy issue to deal with, at least not with Java.
[ March 21, 2006: Message edited by: Ken Blair ]
 
Paul Clapham
Sheriff
Posts: 22832
43
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:
An AreaCode, afterall, does not necessarily have a state with numbers outside the U.S.
In particular an area code in Canada can extend into more than one province or territory.
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ken Blair: Another choice is to create a method hasAreaCode() which returns a boolean and then force clients to use that before attempting to get the area code.

That is exactly what I had none before posting and learning about the NullObjectPattern. I disliked it also which was the reason for my post.


Ken Blair: Also, I'm not sure I would put the state and locality in the AreaCode. Rather, I think I would provide a separate class that could lookup the state (if any) of an AreaCode. An AreaCode, afterall, does not necessarily have a state with numbers outside the U.S.

Paul Clapham: In particular an area code in Canada can extend into more than one province or territory.

Well thank you guys for exposing the weaknesses in my curent implementation. Now I have to go back and rethink my design. Remind me not to expose the details of me EmailAddress class.
[ March 21, 2006: Message edited by: Garrett Rowe ]
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The "null object pattern" exposes implementation ...


Or depending on how you look at it, Null Object hides implementation. What behavior should change when area code is null? If you encapsulate that behavior in two implementations of the PhoneNumber interface nobody will ever need to know the details. This may require a "tell don't ask" design where you cannot "ask" getAreaCode() but only "tell" doSomethingWithAreaCode() and that's not always easy or attractive, either.
 
Jeff Albertson
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Paul Clapham:
In particular an area code in Canada can extend into more than one province or territory.


That reminds me of a posting here where someone was trying to take their "92" |-> "ninety-two" code and make it international, until it was pointed out that "92" |-> "quatre-vingt-douze" ("four twenties and twelve"). Another example is that you may want to handle the single and plural form of a noun, like "child" and "children", but then again, some languages have more forms, like singular, dual (for two) and plural (for three or more).
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Stan James:


Or depending on how you look at it, Null Object hides implementation. What behavior should change when area code is null? If you encapsulate that behavior in two implementations of the PhoneNumber interface nobody will ever need to know the details. This may require a "tell don't ask" design where you cannot "ask" getAreaCode() but only "tell" doSomethingWithAreaCode() and that's not always easy or attractive, either.


The implementations I've seen require an isNull() in the interface. Asking an object whether or not it's a specific implementation (a 'null' implementation) smells horrible to me. The benefit I see coming from a null object is avoiding NullPointerException and allowing the behavior that should be expected when something doesn't exist to be put into the null object. For example, a toString() that displays "???" or "" or whatever. Once you reach that point I'd almost rather just set it to "???" or "" to begin with, but that smells just as bad and has other problems. Personally I lean towards using hasAreaCode(), but rather than throwing an exception if getAreaCode() is used returning an empty String, or whatever.

So my PhoneNumber might look like this:



and part of the implementation might be:


[ March 22, 2006: Message edited by: Ken Blair ]
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Agree, the isNull() is ugly and shouldn't be there. The goal is to make it unnecssary to know.

My most recent real life Null Object was inspired by training material for a vendor framework. The book said: to make an object work in CMP or BMP you must avoid calling methods on an already active transaction, so do this:

I did this instead:

The NullTransaction methods do nothing at all but it eliminates tests for null, risk of NPE, etc. I'll never need to know what implementation I have. After getTransacation() it is all "tell don't ask"; I don't getAnything() from the NullTransaction. If I did it would have to return something appropriate.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
But how would you implement this in the OP's case?
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hmmm, I thought I just edited this comment on my last post but I don't see it ... The Transaction has a lot more behavior than a PhoneNumber DTO, so it's easier to talk about appropriate behavior for a null.

For the OP's problem, I'd try to eliminate getAreaCode(). Who needs that and why? Can the PhoneNumber do that task instead?

Failing that on a DTO or behavior-free bean, I'd try replacing null with spaces in the getter().

Failing that if null really has a distinct business meaning different from blank, I'd live with the null.

Last to pop in my head but perhaps best (!) make RealAreaCode and NullAreaCode classes that implement the AreaCode interface. Now the translation of null to blank or whatever behavior is appropriate is usable every single time you have an AreaCode, not just in PhoneNumber. That occurs to me last because the rule some live by "Make a domain smart class for every field, never use primitives or Strings" is uncommon. I don't do it.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hmmm, I thought I just edited this comment on my last post but I don't see it ... The Transaction has a lot more behavior than a PhoneNumber DTO, so it's easier to talk about appropriate behavior for a null.


I'm not so sure. In your example it seems like you don't actually care whether or not it's the null implementation. That's somewhat simpler than cases where you have a distinction between whether or not something exists and how it's displayed.

For the OP's problem, I'd try to eliminate getAreaCode(). Who needs that and why? Can the PhoneNumber do that task instead?


What if the database has a separate column for area code? What if you need to sort by area code? What if you need to do a lookup to find a state, region, etc. for an area code? How about displaying the area code in a variety of different formats? I don't think it's realistic to expect PhoneNumber to be able to account for everything you might need to do with it.

Failing that on a DTO or behavior-free bean, I'd try replacing null with spaces in the getter().

Failing that if null really has a distinct business meaning different from blank, I'd live with the null.


I don't see how there can't be a distinction between whether or not a phone number has an area code, or more precisely, whether or not that area code is known, and how it should be displayed.

Last to pop in my head but perhaps best (!) make RealAreaCode and NullAreaCode classes that implement the AreaCode interface. Now the translation of null to blank or whatever behavior is appropriate is usable every single time you have an AreaCode, not just in PhoneNumber. That occurs to me last because the rule some live by "Make a domain smart class for every field, never use primitives or Strings" is uncommon. I don't do it.


Then you're back to forcing the client to check which implementation it is when they want to know if the area code is known/exists.
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well for my AreaCode interface that I have so far looks like this:


And in the NullAreaCode implemetation all the methods return zero-length strings. The advantage is that it is NPE safe, however in eliminating a test for null values, the calling method must now check for zero-length strings.

I still haven't taken into account international AreaCodes so this interface is likely to change.
[ March 22, 2006: Message edited by: Garrett Rowe ]
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I would suspect you'll find the entire AreaCode interface to be extraneous when you do.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!