views:

195

answers:

4

Here's a question to expose my lack of experience: I have a method DoSomething() which throws an exception if it doesn't manage to do it cleanly. If it fails, I try the less accurate method DoSomethingApproximately() several times in the hope that it will find a sufficiently good solution; if this also fails I finally call DoSomethingInaccurateButGuaranteedToWork(). All three are methods belonging to this object.

Two questions: first, is this (admittedly ugly) pattern acceptable, or is there a more elegant way?

Second, what is the best way to keep track of how many times I have called DoSomethingApproximately(), given that it is likely to throw an exception? I am currently keeping a variable iNoOfAttempts in the object, and nesting try blocks... this is horrible and I am ashamed.

+2  A: 

Return an error code instead of throwing an exception.

If the ways those methods fail do throw exceptions, catch them all in the same method and take appropriate action, like increasing a counter and returning an error code.

   bool result = DoSomething();
   while (!result && tries < MAX_TRIES) {
       result = DoSomethingApproximately(); //This will increment tries
       if (tries > THRESHOLD) {
           result = DoSomethingThatAlwaysWorks();
       }
   }
Vinko Vrsalovic
+4  A: 

You should never use exceptions for control flow of your application.

In your case I'd bunch the three methods together into a single method and have it return which particular approach succeeded, maybe with an enum or something like that.

Niklas Winde
I disagree. if DoSomethingApproximately() and DoSomethingThatAlwaysWorks() are actually error-handling functions, then exceptions may very well the appropriate way to do it.
Anders Sandvig
Exceptions are to be thrown in unexpected situations, not in expected errors. Of course there are exceptions to this rule as well.
Vinko Vrsalovic
Yes, of course. We must assume that DoSomething() does what it's supposed to do, unless something unexpected happens... ;)
Anders Sandvig
No, we must assume nothing. The questioner even seems to expect that the method will fail...
Vinko Vrsalovic
Beyond that, you have to be careful you aren't hiding exceptions which are actual problems and not 'expected' problems. Regardless, +1 don't use exceptions for flow control.
sixlettervariables
I only expect it to fail very rarely. But I take the point about flow control.
Joel in Gö
+2  A: 

How about (pseudocode):

try{ return doSomething(); }
catch(ExpectedException) { ...not much here probably...}

for(i = 0 to RETRIES){
try{ return doSomethingApproximately; }
catch(ExpectedException) { ...not much here probably...}
}

doSomethingGuaranteed();

Addendum:

I strongly recommend that you do not use special return values, because that means that every single user of the function has to know that some of the return values are special. Depending on the range of the function, it may be sensible to return an ordinary part of the range that can be dealt with normally, e.g. an empty collection. Of course, that may make it impossible to distinguish between failure, and the "right" answer being the empty collection (in this example).

Marcin
+2  A: 

Go the whole function pointers in a structure route. Just to spice it up, I'll use a Queue and some LINQ.

Queue<Action> actions = new Queue<Action>(new Action[] {
   obj.DoSomething,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingApproximately,
   obj.DoSomethingGuaranteed
});

actions.First(a => {
   try {
      a();
      return true;
   } catch (Exception) {
      return false;
   }
});
Mark Brackett