Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Should methods return nulls or throw exceptions 3

Status
Not open for further replies.

AndrewJMarshall

Programmer
Jun 8, 1999
24
US
I know it seems like a goofy question, but ...

I'm writing a data layer class that provides methods to retrieve objects from a database by their ID. If no record exists for a specific ID, should the methods provided return null or throw an exception?

Please provide your reasoning as this is a mostly academic question. My suspicion is the that the "right" answer is "it depends". 8-(

Thanks,
Andrew
 
Guys, I don't think NOT finding a record should be considered an exception. Do you?

Andrew was simply asking if he should throw an exception or return null if a record is not found. Trojan stated it correctly IMHO.

Brian

Spend like you don't need the money,
love like you've never been hurt and dance like nobody's watching!
 
<Guys, I don't think NOT finding a record should be considered an exception. Do you?

Well, yes, I do. Why not? To me, having several different ways to handle different types of unwanted behavior leads to less maintainable code. How do you justify the hit to maintenance overhead? That's been my point all along.

Bob
 
Let's say that you have an accounting database. Let's also say that the user has selected a data range for viewing invoices. Let's also say that the date range is very short and that for that period there are no invoices in the database.
Are you trying to say that this is an error? That the program should go bang and treat this as a failure? I would suggest that it is perfectly acceptable to show a page with no records since there are no records for that period. I do not see that as an error.
If, on the other hand the program runs a trial balance and divides by zero I would suggest that this should be treated as a real error. Something unexpected has happened and needs to be handled and I think that is the point here.



Trojan.
 
<Are you trying to say that this is an error? That the program should go bang and treat this as a failure?

I'll answer "no" to your question, and assume that it isn't as disrespectful in intent as it is in appearance. For further detail, I suggest you reread my previous posts.

<Something unexpected has happened and needs to be handled and I think that is the point here.

I'll answer this with a quote from my previous posts:
Given that one catches exceptions and not something else, and that one can't catch an exception that one doesn't expect, espousal of the first statement constitutes repudiation of the second.

I just can't seem to make this point. If you don't expect an error, all you can do is throw it. How can you catch an error, if you don't know what the error is? So, if the way to handle errors that you expect to happen is by some other method, then it follows that you should never use catch, but should find another way to do it. I believe I've made this clear before, and have yet to be shown a flaw in my logic.

Now, you explain about trial balances and division by zero. Well, you're expecting that to happen, or you wouldn't be talking about it, just like you're expecting the no data found to occur. Why is division by zero intrinsically an error, and no data found intrinsically not an error?

That's question 1. Question 2 is what difference between these two errors/exceptions/whatevers is so profound as to justify entirely different architectural approaches to each?

Those are the questions I keep asking.

Now, I'm going to quote some things from Francesco Balena's book Applied Microsoft .Net Framework Programming in Visual Basic.Net, Microsoft Press, 2003. Balena is the MSDN director for Italy, the author of a number of excellent books, and knows more about this subject than any of us are ever likely to.
A line of code can fail for many reasons...without using exception handling, it's difficult...to write code that gracefully detects and recovers from these failures. Sprinkling the code to detect these potential failures into your application's main logic makes the code difficult to write, understand, and maintain. In addition, having code that checks for these potential failures would be a huge performance hit on an application.

Using exception handling, you don't need to write code to detect these potential failures. Instead, you can simply write your code assuming that the failures won't occur...Then you put all your recovery code in a central location. Only if a failure occurs does the exception handling mechanism step in...

And another:

Another common misconception is that an "exception" identifies an "error"...however...it isn't possible for the developer...to know when the caller has called the method incorrectly for the application. Only the developer calling the method can determine this, and therefore only the caller can decide if the results of the call indicate an "error." So you should avoid thinking, "I'll throw an exception here in mhy code to report an error."

One more:

An exception is the violation of a programmatic interface's implicit assumptions...How does a method notify its caller that one of its assumptions has been violated? It throws an exception...violating a programming interface's assumption isn't necessarily a bad thing. In fact, it can be a good thing because exception handling allows you to catch the exception and gracefully continue execution.

These are better explanations of what I'm trying to say than mine are. Using his "violation of implicit assumptions" (a big improvement on my "unwanted behavior") terminology, it's clear that nearly all of the posts in the entire thread describe behaviors that can be described as such. I would make a case for consistent, centralized handling of such violations, without putting value judgments (error/bad or not error/good) on them.

To sum up, My point of view, with a little help from Balena, is:

1. Exceptions are programmatic actions or occurrences that violate the assumptions of the context in which they occur.
2. All errors are exceptions.
3. Exception handling should be encapsulated from the rest of the application, providing an interface to its functionality.

That's what I've been trying to say, I guess.

Bob


 
Now, you explain about trial balances and division by zero. Well, you're expecting that to happen, or you wouldn't be talking about it, just like you're expecting the no data found to occur. Why is division by zero intrinsically an error, and no data found intrinsically not an error?
Ok, let's try to explain.
If you have code that requests rows from a DB and zero are returned, is it possible to detect this in your code simply by saying something like "if(rows) blah blah blah"?
Of course it is. It's trivial to handle and in some cases you don't have to handle it at all. For example, If you loop "1 to rows" then your loop will never execut and if that loop simply populates rows of a table then the user sees an empty table which is fine.
Now for the other side of the coin. Division by zero.
Are you likely to expect a possible division by zero on all calulations you do? No. Would you like to test every single variable every time you use it for any kind of calculation? No. What happens if you don't? Bang.
There's the difference.
You can safely let the zero rows loop around zero times safely and all is well. It's not a serious error.
The divide by zero though will completely crash the program unless you have something to "catch" it.

Does that make the point any clearer?


Trojan.
 
Let me juxtapose a couple of quotes. First from Balena:

Sprinkling the code to detect these potential failures into your application's main logic makes the code difficult to write, understand, and maintain.

Now you:

If you have code that requests rows from a DB and zero are returned, is it possible to detect this in your code simply by saying something like "if(rows) blah blah blah"?
Of course it is.

In other words, the experts agree with me, not you, assuming that you're saying it's not only possible, but recommended.

See, it's not that I don't understand what everyone's saying. It's that I don't agree with it, and I'm asking you to back up your point of view. For example, you justify your latter case by implying that one wouldn't like to test a single variable every time you use it for any kind of calculation. (No, I wouldn't.) Why do you then suggest that one WOULD like to use this tactic in your first example? Why is it different? So far, you've basically said "well, because it isn't an error." No disrespect intended, but why isn't that tantamount to saying "well, because it just is?"

So, Trojan, I believe you've explained your position very clearly. What I don't believe is that you've made any strong arguments to back it up, whereas I believe I've made some strong arguments to back up a contrary position.

All I've been doing ever since I've entered this thread is made two points, and I would say I've backed them up pretty well. The opposition doesn't seem to be able to back up its position at all.

It's kind of like this. Suppose someone says "look at my house." And I say "I'm sorry, but the house is based on faulty architecture, and it's about to fall down." So someone says "gee, you don't understand. Let's take a closer look at the house, in the hope that you will understand better what the house is." And I say, "let me try to help you understand better why it's falling down." And the other says "boy, he really doesn't understand. Let me show him all the other houses on the block just like it. Maybe he'll get it then." And I say "sorry, these houses are falling down too." And I bring in an architect to explain why. And the owner of the house says "Ok, let's try to explain......"

Am I getting through here? All I want to hear is someone say "No, the house isn't falling down. Rather we're using the following alternative architectural method, which, as I will demonstrate, is as sound as the one you're espousing."

In other words, please quit explaining what and start explaining why. :)

I'd like to say this, to be clear. Please understand that, although I don't agree with what I'm hearing here, and am cheerfully butting heads with maybe every other poster in this thread, that I feel nothing but respect (well, a little irritation too, but that's respectful irritation) for everyone here. If I have in any way communicated otherwise, I have failed to communicate my true feelings. The fact that anyone here can talk intelligently about this subject at all suggests considerable professional accomplishment, and is to be commended.

Bob
 
Bob I'm afraid I just can't understand you.
You're going on about houses and I have not a clue why.
The point I made should have been simple.
You seen no difference, I do.
The difference is that if I do not catch the errors, one will make the app go bang and the other will not. Indeed, it will cause no problems at all and actually give exactly the expected results.
Can you not see that? (And I don't need any houses to prove thie point!!!1 ;-) )
I am amazed and at a loss.
What more can I do to explain it to you?


Trojan.
 
Well, ok. I guess we just don't understand each other then. I'm trying to say that the methodology of handling different types of errors/exceptions/whatevers in different ways according to taste represents an inconsistent and scattershot approach to handling violations of assumptions. I'm also saying that nobody agrees with me here, and for no better reason than they do it differently. My house analogy I find to be quite apt, but I might be alone in that assessment.

For the record, I'll compare our positions.

My position:
1. A consistent approach to dealing with situations that violate an interface's implicit assumptions makes for more maintainable and better-performing code.
2. The try/catch/throw paradigm is in place and represents just such a consistent approach.
3. To intermingle multiple paradigms is inconsistent, and therefore not supportable unless there is strong and compelling reason to do so.
4. Though taste and habit may be compelling reasons to some, they are not strong reasons.

Your position:
1. Errors that make the app go bang need to be handled differently from errors that do not make the app go bang, because those errors aren't really errors anyway.
2. Everybody ought to understand this without any further explanation.
3. This position refutes Bob's position.

Feel free to clarify your position, if I haven't done justice to explaining it on your behalf. If not, I'll leave it to posterity to decide which is the more robust architectural point of view.

Best regards,

Bob
 
I think the obvious fundamental difference is that you see zero rows as an error that must be handled whereas I do not.
I see it as perfectly acceptable for there to be no avaiable data for a particular request and that this situation does not automatically have to consitute a serious error that must be thrown as an exception and handled.
! noticed that you made 4 apparently reasonable claims for your position and one reasonable claim for mine plus two ridiculous ones.
This does not help your case, it merely proves that you have no interest in seeing both sides of an argument.
I wonder, just out of interest, how much actual real world development have you done?
You are classed as an Instructor, does that mean that you read books and echo them or that you acutally use the info in the real world?
That is not meant to be a snipe, just an honest question in an attempt to understand your reasoning.


Trojan.
 
<I think the obvious fundamental difference is that you see zero rows as an error that must be handled whereas I do not.
>I don't see it as an error necessarily, I see it as a situation that needs to be handled, and advocate a consistent means of doing so. Reference the second of the above quotes.

<I see it as perfectly acceptable for there to be no avaiable data for a particular request and that this situation does not automatically have to consitute a serious error that must be thrown as an exception and handled.
>Serious implies a value judgment. Your statement goes over past ground. I defer to my positions as already stated.

<I noticed that you made 4 apparently reasonable claims for your position and one reasonable claim for mine plus two ridiculous ones.
>I must have done better than I thought I did. I didn't find any of my claims for your side of the argument reasonable. Reasonable claims for your side of the argument are not my job, but yours, as far as I'm concerned. Feel free to provide same.

<This does not help your case, it merely proves that you have no interest in seeing both sides of an argument.
>Au contraire. I'm very interested in seeing your side of the argument, as evidenced by my repeated requests for you and others here to provide it. I repeat my invitation.

<I wonder, just out of interest, how much actual real world development have you done?
>Um, a great deal, although I would remind you that an argument's merit is not determined by the perceived value of the arguer, which is intrinsically arbitrary.

<You are classed as an Instructor...
>That's because I teach.

<...does that mean that you read books and echo them or that you acutally use the info in the real world?
>Yes, it does. :)

<That is not meant to be a snipe, just an honest question in an attempt to understand your reasoning.
>Of course not. Not taken as such. Coming from a family of lawyers, I think I would know an ad hominem argument if I saw one, after all, let alone three. And no, I was not laughing. I just inhaled a little cauliflower....

HTH

Bob
 
Ok, ok, let me give an example of what I'm talking about. Say you want to put some stuff in a text file. If the file already exists, you want to append to what's there. If it doesn't, you want to create it. Now, you could do this in many ways. Here are two of them:

(1)
1. Check to see if the file exists. If it doesn't, create a new one. If it does, open it.
2. Append your stuff to the file, etc.

(2)
1. Try to open the file. If it isn't there, create a new one.
2. Append your stuff to the file, etc.

The second one is an example of my argument, the first an example of the contrary one.

Version 2 is clearly more efficient. The more often the file exists ahead of time, the more efficient it is. Version two assumes the file exists, and only does something "exceptional" if it doesn't. In other words, a file not found exception occurs because the situation has violated the assumption of the interface. That is NOT an error, on that point both sides agree.

Version 1 checks to see if the file exists every time, whether it does or doesn't. Every time the file exists, version 1 has overhead that version 2 does not. This is inefficient, and I just can't justify the argument that such inefficiency is supportable simply because there isn't an error, so the situation has to be handled differently than when there is. Why have two and more ways of doing things when one will do both ways better? How does one justify the code and maintenance overhead? If someone can, please speak up, as I have requested many times now.

Bob
 
Can you offer real code examples of your two scenarios?
In my experience, if one wanted to append to a file one would open the file for append and the OS would normally create the file, if it was not there, automatically.
Are you thinking in some specific language?
Code:
row = sql("select * from invoices where date >= start and date <= end_date");
display_page_header();
foreach row in rows {
  display_row(row);
}
display_footer();
Some pseudo code for you to comment on.
Why do I need to throw an exception here?
If no rows are returned, the page still displays perfectly, there are just no invoices to view.
Following your argument we would have to add a test for zero rows, throw an exception on that zero and write some code to handle the exception.
Also you then have the main flow of the code interrupted and the user will probably have to see something different (an error message or the like). This is all a lot more code than is necessary, and would alter the users perception of the program for no reason that I can see.
Oh, and on the subject of efficiency, which version is more efficient? My suggestion that needs no extra code at all or yours that does?
I feel like I'm repeating myself here but I don't see that you've answered this argument at all yet.
Maybe that's the skill of a lawyer! ;-)


Trojan.
 
>if one wanted to append to a file one would open the file for append and the OS would normally create the file

In which case you are actually (thanks to the API call) implicitly handling an exception. You just don't properly appreciate that you have done so because the handling is all done by you choice of call. It's all a question of perspective; exceptions are not necessarily errors.
 
I beg to differ.
The OS returns a failure value if it cannot open the file.
Granted it might be better form if it did actually throw an exception in this case but I don't agree that returning a null pointer or flag is exception handling in the normally accepted sense.
BTW: I do know how kernels work, I've written kernel device drivers, languages, databases, firmware, comms systems and all manner of things in my time. Or maybe that's just one of your "ad hominem" arguments! ;-)

Also I see you have completely evaded my questions and points yet again.
Are you sure you're a lawyer and not a politician? (Maybe both?)


Trojan.
 
<Can you offer real code examples of your two scenarios?
In my experience, if one wanted to append to a file one would open the file for append and the OS would normally create the file, if it was not there, automatically.

Well, that's true in my experience, too, so maybe it's not the best example. We'll take your pseudocode and alter it a bit, to provide a better example. As for real code examples, my main expertise is in VB6, and attempting to demonstrate these techniques in VB6 wouldn't serve to clarify them. As Appleman says, VB6 error handling sucks.

<Following your argument we would have to add a test for zero rows, throw an exception on that zero and write some code to handle the exception.

Actually, I would have a data reading object whose job it was to throw exceptions typical in its use do that testing and throwing. Of course, it wouldn't write any code to handle the exception. Whether the user of the object handles it would depend on whether the user's assumptions have been violated by its occurrence or not.

<Why do I need to throw an exception here?
You don't! However, that fact doesn't support your argument! All you're really saying in that it doesn't need to handle a zero row response differently from a nonzero one. As such, it isn't an example of what we're debating about. I'm not suggesting that you have to throw an exception when the interface's implicit assumptions are NOT violated, and as far as I can see, that's not what you're trying to refute.
<Oh, and on the subject of efficiency, which version is more efficient?
Why yours is, most certainly. Of course, it doesn't represent your side of the argument, QED.

I'm going to use your pseudocode to provide a better example than my own, as I promised:
Code:
//Your argument:
row = sql("select * from invoices where date >= start and date <= end_date");
/*
if there are no rows then
   {
   tell user, invite to change dates
   }
*/
display_page_header();
foreach row in rows {
  display_row(row);
}
display_footer();

Code:
//my argument:
try
   row = sql("select * from invoices where date >= start and date <= end_date");
catch NoRowReturnedException
   tell user, invite to change dates
end try
display_page_header();
foreach row in rows {
  display_row(row);
}
display_footer();

This also supports my last. I'll repeat it again: exceptions handle behavior that violates the assumptions of their associated interface. Exceptions are not to be thrown when they do NOT do so. Your example does NOT do so; one of the assumptions of the interface is that it is not required that rows be displayed.

As for your last post, it appears to be based entirely on the perception that strongm and BobRodes are the same person, and so I will refrain from further comment, assuming it was posted in error. Except for making the observation that I'm unaware of anywhere in my posts that I've made an ad hominem argument, so I invite you to show me. Further, I'm unaware of anywhere in my posts where I mentioned that I was a lawyer. (Actually, I'm a musician, and since I'm so fond of analogies, the fact that I study scales and arpeggios and music theory doesn't suggest that I can't play Chopin and Beethoven and the blues. Rather the opposite, I would think.)

Since it's not clear from your use of the term that you and I have the same understanding of the term ad hominem, I'll state mine here. An ad hominem argument is one that attempts to lend weight to a position by establishing the value of the holder of the position rather than the position itself, or conversely, to trivialize the position by reducing the value of its holder. Of course, such arguments are spurious; their value lies in using emotion to distract focus from the issue at hand.

Examples from your posts include:
<I do know how kernels work, I've written kernel device drivers, languages, databases, firmware, comms systems and all manner of things in my time.
<you have no interest in seeing both sides of an argument.
<I wonder, just out of interest, how much actual real world development have you done?
<You are classed as an Instructor, does that mean that you read books and echo them or that you acutally use the info in the real world?
<Are you sure you're a lawyer and not a politician? (Maybe both?)

I'm not able to find examples in my posts. Perhaps you can. In any case, perhaps you will refrain from making them in future, as they do not add to the quality of the thread. Furthermore, if you find examples in my posts, please point them out, so that I can also refrain.

Now, I believe I've pretty well stated my case at this point. I find that I have supported my point of view quite well, I don't find that you've supported yours well enough to change mine and adopt it. Your showing that my example was not the best does not suggest to me that you have refuted its underlying assertion. The example certainly doesn't do so. If you, or anyone else, can provide fresh fuel for the debate, please do so.

As for begging to differ with strongm, that should be interesting. I shall stay tuned.

Bob
 
I must apologies for mixing the two of you up. You are indeed correct on this point.
I'm not suggesting that you have to throw an exception when the interface's implicit assumptions are NOT violated, and as far as I can see, that's not what you're trying to refute.
That was indeed exactly the point I was trying to make.
The thread asks the question:
If no record exists for a specific ID, should the methods provided return null or throw an exception?
My answer is that it depends. Yours appeared to be that you must throw an exception. I have been repeately trying to explain why you might not wish to throw an exception and your argument to refute mine is to suggest that this was not what i meant in the first place!
All you're really saying in that it doesn't need to handle a zero row response differently from a nonzero one. As such, it isn't an example of what we're debating about.
It is exactly what we are debating.

BTW: the "ad hominem" comment was meant to be taken as "tongue in cheek" (hence the smiley) and was innapropriate for the reason you pointed out earlier. I did indeed not notice that someone else had entered the conversation. Mistake on my part. I did not, however, need a definition.


Trojan.
 
<It is exactly what we are debating.
It may be what you're debating, but not what I'm debating. Again, my point addresses the handling of unwanted behavior, behavior that violates the assumptions of the interface, call it what you will. Your example shows wanted behavior, behavior that does not violate the assumptions of the interface.

I agree with your original answer. It does depend. I disagree with what it depends on. I did not suggest that you must always throw an exception. I suggested that it's most consistent to throw exceptions when they occur, rather than having more than one way to handle them. Any reading of my posts should verify this.

I'll take one of your earlier examples, and clarify my response to it. Let's take your accounting application example. Let's say that you're looking up a customer, to see what open invoices they have. If they don't have any, you get a blank screen. Well, that's fine. No exception. No error. No whatever. Because the assumptions of the interface include the possibility that no invoices will be found. I've never disagreed with that. That doesn't constitute unwanted behavior, to use my original terminology.

But, what if, when there are no invoices found, you want to, let's say, beep at the user and message them? Now, we're getting into what I consider the area of the debate. Here's where I would start using the no records found exception that's thrown by the object providing the data, rather than checking in my own code for a null value. All of my copious responses are based on that premise.

This quote
If you have code that requests rows from a DB and zero are returned, is it possible to detect this in your code simply by saying something like "if(rows) blah blah blah"?
Of course it is. It's trivial to handle [the subject we are debating: here you imply that "handling" is done in a manner other than exception handling] and in some cases you don't have to handle it at all[the subject we are NOT debating]...
Clearly from this quote you're not drawing a distinction between the two, with respect to how they should be handled. So I can see why you might feel that the "not handle it at all" part is an example of the subject at hand. It isn't, in my view. I'm drawing the distinction, and have been all along.

Bob
 
I don't think there is any need for that.
I think we are actually arguing different points and probably have been since the start.
My point is and always has been that Zero rows returned does not have to be considered as an error at all. I stand by that point and I believe it is the more appropriate aregument of the two but that's just my opinion. The point is I think we both agree on my argument.
Bob's argument appears to me to be that if one considers zero rows to be an error condition that should be treated in a different way to the norm then he suggests it should be treated as an exception. I do not have a problem with this at all, indeed I was the one who tried pointing people to a document written by Damian Conway that suggested exactly this point so I think Bob has simply misunderstood what I've been trying to say right from the start.
If Bob is happy with my description of our two arguments then I think we both actually agree with each other's arguments!
Shock Horror! Wonders will never cease! ;-)



Trojan.
 
Well, perhaps this has been nothing more than a study in the fine art of miscommunication. If so, it's all Trojan's fault, of course. :) On the other hand, this thread has helped me to clarify my point of view, and perhaps it will help others to do the same.

For the record, Trojan, I agree with the positions stated in your last post.

chip, I'm certainly in agreement with YOUR last post.

Best Regards to all,

Bob
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top