Take advantage of Eiffel Exceptions as Objects
- Tags:
- Exception handling
I was asked what the benefit of Exceptions as Objects (EAO) was in Eiffel. My short answer was flexibility of design. I knew that this answer was far from sufficient. Now I take the time my machine is busy running tests to think about it deeper and write it down. Hopefully people who are trying using it or starting to learn this area of Eiffel may better understand the mechanism and its benefits. What I am talking maybe far from sufficient too and don't blame me I know I am not a good English writer.
Before we go into the subject EAO, I would talk about some basics of exception handling and the previous way of exception handling in Eiffel.
Real applications normally have the situations they run into exceptional context in which they don't have enough information to deal with such exceptional cases. Hence they have to do something to flag the rest of the world possibly with the interesting information of exceptional contexts. Informing in applications implies handling, though exception handling could be nothing. Without good exception handling mechanism, coding to handle various exceptional cases is a nightmare and the code is almost unreadable. A good exception handling mechanism perfectly decouples the code for normal application processes and that for exception handling. Thank Eiffel for its born readability. The built-in rescue-retry exception handing mechanism has been good enough to achieve the decoupling. Codes are well arranged and of really nice readability. Consider the following piece of code:
Example1
In Example1, an exception is raised in normal_process' with the tag of "Not initialized." (It is call "message" in EAO), when not
is_initialized'. For normal_process', this is an exceptional context, it doesn't know how to fix this, so raises it to higher level that knows better the context and how to handle the not
is_initialized' exception. Now rescue of make' handles the exception.
is_initialized' is simply set with True. One may argue that normal_process' here knows enough information to handle the case not
is_initialized'. But the reality is much more complicated, where is_initialized' may not be a field or the
normal_process' is a client of a library in which exception is raised.
In Eiffel, once an exception is raised, the calling routine aborts, the runtime backtracks the call stack to find the nearest rescue and step into the rescue. If no rescue is found, backtracking reaches the bottom of the call stack where a execution vector with a hidden rescue was pushed. The hidden rescue of course is executed to do necessary clean-ups and also to print the exception call stack. That 's what people usually see in console if an Eiffel application crashes.
Exceptions as Objects (EAO)
Now EAO is something new, but keep in mind the rescue-retry mechanism is not obsolete. The way of rescuing and internally backtracking don't change at all. The ultimate change is the ability of information encapsulation from which we will benefit a lot. I will talk about it later. The new mechanism, as the name implies, is based on objects. In this article, http://dev.eiffel.com/Exceptions_as_Objects , one can see the exception hierarchy, interfaces and some other topics. EXCEPTION is the top most class in the hierarchy, which means all exceptions derive from it. {EXCEPTION}.raise raises the exception object. In previous way of EXCEPTIONS class, take the Example1 as an instance, the exception raised was only a code `developer_exception' defined in the class EXCEP_CONST and the tag, a string of "Not initialized.". Raising exceptions in Eiffel was almost fully governed by the class EXCEPTIONS. Apart from this class, nothing exception related had to do with objects, it was code based exception handling.
Example2:
is_initialized' in
normal_process', the developer defined exception is created by developer, filled with exceptional information "Not initialized" and raised by calling raise'. Once
raise' is call, the following code, if any, in that routine wouldn't be called. The nearest rescue is hit in make'. The exception object, containing exceptional information, can be accessed by calling {EXCEPTION_MANAGER}.last_exception. With the exception object,
handle_my_exception', who knows better how to handle the exceptional context, is able to do more if needed.
From the whole system perspective, what is the EAO doing? The answer is simple. It provides a universal way to store and access exceptional context information wrapped as objects, and the scope is from deep into the runtime to very top of the application. Example2 demonstrates how developer exceptions are raised and get handled. We can think about what it is the situation if NON_INITIALIZED_EXCEPTION, is_initialized' and the call
normal_process' is defined in a library. We find that it is the same thing, but better explains how one benefits doing the library when he knows nothing about how the situation should be handled by its client application. Responsibility is brought in here and very clear that who is responsible to collect exceptional information and who is responsible to handle it with that information. Maybe one is more interested in system raised exceptions. From my point of view, there is nothing really special compared with those raised by developers. The difference is that one particular Eiffel compiler with its runtime might have different implementation, and those system raised exceptions are created and raised by the runtime or by code generated from the compiler. System raised exceptions could be raised anywhere in the code, in most cases, they implies bugs. Finally, about system raised exceptions, some of them, such as operating system failures, are actually a handler in the runtime who does mappings between exceptions raised from the OS and the language exceptions.
As the idea of exception handling is introduced simply, hopefully not too simple, I can go ahead the benefits of EAO as I can see.
Benefits
Encapsulation
As stated earlier, compared with the previous implementation of exception handling in Eiffel, the ultimate enhancement of EAO is information encapsulation. People benefit from object-oriented way of this good manner of information encapsulation. Code is clear, when all exceptional information is encapsulated in the exception object which is directly accessible later through EAO mechanism when handling. Let's have a look at complexer example
Example3:
In library "database":
Of course Example3 is code from a real system, there are a lot more things we need to do within real systems. And I don't add any contract in this piece of code, since I want to focus more on the exception handling. In this example, we see domain, name and passwd as exceptional context which is saved in the exception object CONNECTION_FAILURE. In this exceptional context of the library "database", there is no information how it should proceed, so the exception is raised to upper level. We can think about how we did with old implementation of exception handling. What we supposed to do was simply call `raise ("Connection failed!")'. At the client side in rescue, only information of the exception code and message was available. People could have their own way work around. He could put information like domain, name and passwd in the DB_READER as queries or had his own class to wrap this information and make it available somewhere accessible in the library. But as you see, those ways working around were awkard, since for clients, it was difficult to know where the useful information was. EAO now does the good job, it is a standard way for the library developers to encapsulate exceptional information and for the clients to get the information.
Extendibility
This is what we benefit from object-oriented method. Inheritance, polymorphism and so on. The previous way of exception handling only have one code for developer exceptions. That's far from enough. Now one can use the type system to define it own exceptions as many as he may want, as long as it inherits from DEVELOPER_EXCEPTION. Still use Example3, in the "database" library, there can be many kind of connection failures like DB2_CONNECTION_FAILURE, ORACLE_CONNECTION_FAILURE and so on. They all inherit from CONNECTION_FAILURE. At client side, there is no particular change should be done if details are not that important. One may also notice that with this extension, there is no need for the client to know each kind of exceptions. But in previous way, one may have to write code like this:
Maintenance
With object-oriented method, it has been proved that code are can be easier maintained. I don't think I need to talk about this, there are a bunch of books about it.
Better Debugging
ISE debugger has supported EAO, which means that it will be easier to debug when with the caught exception object in the debugger. Since exception objects are proper places to collect informative exception context. With a single object view, one can get useful information like the trace and, more important, the information filled by the coder who intended to expose it.
I am stopping here. I am too lazy to explore more. But I will extend it if I find more.