📜 ⬆️ ⬇️

Exceptions and myths about them

This is not the first time that I encounter an inflexible attitude to raising exceptions. It is to raise, because to intercept the majority of the opinion is the same: intercept only when you can actually handle. Raising is perceived as something exceptional, out of the ordinary. When they see a throw, they start telling a bunch of stories about how ...

Remember how to deal with fear? Catch one minute during which you give free rein to all your emotions. Then you say “enough” and plunge into problems with your head. A minute has passed.

First, let's find out what kind of animal it is. I highlight the following properties:

And that's all. All sorts of losses in performance, the complexity of control are context-sensitive (or more often simply contrived) and require proof in each particular case.

For example: search the file.
')
int FindSymbol(TextReader reader, char symb)
{
char cur;
int pos;
while (cur = (char)reader.Read())
{
if (cur == 'a')
throw new FormatException();
pos ++;
if (cur == symb)
return pos;
}
throw new MyException(); }
}



The following options for exiting the function are defined:

The last version of the function is the most controversial. Usually, programmers are very resourceful in justifying their decisions, and may find a bunch of pros and cons: inefficiency, the client code needs to know the implementation details, the wrong format is an error, etc. All this is true, but not the main thing. The main argument for is sufficiency.

The client no longer needs to analyze a complex result, he asked to find the text - they found him; if not, he won't even know about it.

Another example: the user clicks the cancel button. Again, you can discuss the topic for a long time - why was this mechanism originally created?

But if it is and is suitable for the logic of "cancel" - it is silly not to use it.
When I needed to glue the sills at home, without thinking twice, I used the book “Wpf ...” by M.Mac-Donald as a cargo. So why in other cases do I do otherwise?

How do you feel about small features? I now believe that others simply do not have the right to exist. With such an attitude, there are occasional situations that without raising exceptions, they simply go nowhere. For example, you need to exit the loop located above the call stack. Do not pass the same flag to complete in the end. Or pass ...
If the cycle is not located at the previous level of the call stack, but above? All code without a throw turns into an analysis of the results of the functions. Comes to the ridiculous - methods checking the results of the work of other functions are highlighted. You sit and think about rolling back unexpectedly “incorrect” refactoring. But everything changes when they come: the code starts to be deleted two or three lines at a time (and if I slowed down
long enough, both methods and classes are deleted)

And here is another interesting “flavor” - a large number of conditional operators. When the analysis begins before performing any action, “is it possible to do it”. Let us leave alone algorithms consisting of several operations that separately make the state of the system uncertain. If you started to do - break into a cake, but finish! I'm not talking about them. Although here, if desired, you can find acceptable solutions. So, the smell of a larger number of conditional operators: well, for example, when analyzing the set of input data before performing the operation or checking the validity of the data structure, or ... well, you never know what else. In general, we look at each data element and decide whether it is suitable for our great goals, and then we carry them out. Is not it stupid? You can immediately try to implement our plans. And all that is necessary is to assume that an exception may occur at this site, due to our unwillingness to clutter up the code with garbage.

The main argument of opponents of this attitude to raising exceptions is a performance slump. It is silly to argue with this - the conditional operator is several times more efficient. But there is one “but”: the problem must be proved on real data. Suppose a piece of code is really critical. Oh God! - now you have to spend a couple of hours refactoring. Not days, but hours. Remember how Fowler spoke about his attitude to design: “... I'm trying to determine how difficult it will be to refactor from one design to another. If the difficulties are not visible, then I, without thinking too much about the choice, stop at the simplest. ”

If we assume that we accept this bold idea, then it remains to determine the order of refactoring:
  1. Select the method from the body of the method raising the exception.
  2. We define the criterion in the signature of the selected method, which will show that in a different situation we would raise an exception.
  3. We modify the original method so that, depending on the criterion, it raises an exception. We will call the original method "method with an exception", and selected - "method without exception."
  4. Create an extension method (copy) from a method without exception.
  5. Replace the body of the extension method with the method call with the exception.
  6. We block the propagation of an exception through the extension method and, depending on the ascent, we recharge or reset the criterion.
  7. Add a method to the interface with the method signature without exception. In implementations of these methods, we call the extension method. If the method is not part of the external interface (with respect to the class), so much the better. This item can be skipped.
  8. We replace method calls with exceptions with methods without exception. In interface implementations, this is mandatory (otherwise it should not have begun), in other cases, if desired. The replacement code should look like this: call the method without exception, analysis of the criterion.
  9. Create an extension method from a method with an exception. Its structure should already be true. It remains only to make it work.
  10. Replace the remaining callbacks with exceptions with extensions without exception.
  11. Remove from the interface methods with exceptions and extensions without exception. Extensions with exceptions if possible.


Here is how it will look in the code.


In principle, everything. I hope I managed to dispel at least some of the fears about raising the exceptions, and now when such a need arises you will be more loyal to choose a solution. And most likely, choose a simpler one. So, your receiver will scold you with less frenzy (it’s possible that I’ll be one).

Source: https://habr.com/ru/post/119016/


All Articles