Exceptions, Singleton, Bridge & Logging
|
Exceptions
One of the important aspects of writing an application is error handling. What should a program do if an error occurs? This question is quite broad, the program should react differently to different kinds of errors. Suppose that after a method was invoked on an object, the object is left in an inconsistent state. For example function open on the file class was invoked with a bad file path. What should the program do? Should the program terminate altogether, or maybe report an error and offer a user another try? In many procedural languages functions would look like this:
If some error happens we return from a function with an error code. The code above is oversimplified. In reality we would have to remember to free the allocated resources before every return. The code was quite elaborate, hard to follow. Some programmers resolved this by having less error checking in the code altogether. Some actually used labels to jump to cleanup and then return. Java offers different means of error handling, called Exceptions. Exceptions are better than conventional error handling means because they provide more explicit, compact, robust and direct way to signal errors. In Java Exceptions are part of method declaration:
This sort of declaration makes exceptions part of the interface. The creator of the Algorithm interface is explicitly warning the implementer that in case of failure run method will throw ExecutionException or IllegalDataException. Not only that, but now compiler will force the caller to deal with the exception.
The above illustrates how we may go about catching the ExecutionException. Notice the syntax of try/catch construct. The exceptions are handled in order of the declaration in the catch block. It is essentially translated into:
This is why it is important to declare the catch blocks in the right order. e.printStackTrace is a useful method (defined in the Exception base class), which prints the execution stack.
The stack trace is the sequence of methods on the stack of the currently running Thread. Just as a reminder, lets recall the basic way that stack and heap are organized:
So an exception is thrown when an unexpected error condition occurs.
The exception is caught by an encompassing clause further up the stack.
If the exception is not caught (can be the case with runtime exceptions),
the default exception handler catches it and prints an error message.
Where should we generally catch Exception?
The rule of thumb is simple. If you can handle it in the menaingful way - catch it.
Whatever you do, please never do this:
The way that nested try catch block look on the stack is this:
The inner-most block gets a first shot at catching exception if
its catch clauses match. If not, the stack is unwined further until the
next try/catch block.
As you saw, the Exceptions in Java are objects. The advantage is that we can convey more information
and do better error handling. Consider the following example:
So we can create our own exception classes
by extending the core ones. The base class of all exceptions is Throwable;
it contains a string which can be used to describe an exception. Java offers
two types of exceptions: checked and unchecked. The checked exceptions
are those that compiler can test for during the compilation. The unchecked
ones are those thrown by Java runtime environment. The example would be
OutOfMemoryException, which occurs when the virtual machine memory is exhausted.
All of the exception classes that we create are checked exceptions.
They are all derived from Exception class. Since Exceptions are classes
and since we can create our own we may add methods and fields to them,
and make them carry more information.
Is this useful? As with everything we need to search for the right
balance to maintain sanity. Applications should not be centered around
error handling. Maximum error handling should be placed in the code which
is interacting with the end user.
Errors of type
Runtime error #3256 at 0x34AD546
are not terribly useful for users of graphical interfaces.
Frankly, Bill, we are all sick of them.
When should you create your own exception classes?
You should do it when it will allow you to discriminate
between the different sorts of errors and do better recovery
For example, if you are developing application
framework, you might want to create base class called ApplicationException.
This will be basic exception thrown in the application API.
Then you might add more specific exceptions like ApplicationOpenFailedException,
ApplicationCloseFailedException which will extend the ApplicationException.
For example, if the application reads/saves state, then you will have the following
as a part of application API:
What about situations when you want a certain piece of code to be
executed regardless of whether error has or has not occurred?
Very convenient way of doing this is provided in Java. Every try
clause can be followed by several catch clauses plus finally clause. finally
always gets called in case if error occurs and if it does not. This is
very useful when dealing with streams.
Dont go crazy with exceptions. Exceptions are meant to be used in
case of unexpected errors. For example, consider parsing an integer from a string.
This is a good example of correct usage of exceptions. When bad string
is supplied there is no integer value that can be returned. The only choice
in this case is to throw an exception. Dont be afraid to throw an exception
from a constructor or an init method. Unlike in older versions of C++, this is not a problem
in Java.
Logging
Most applications that are released these days have application logs.
The log is simply a file to which application prints some (preferably) useful
message about the path of execution and errors, which are occurring. It
would be quite useful to design a few logging classes to use in
our project. Here is an extract from a hypothetical log file
Code to do that may look like this:
It makes sense to have one global Log instance
in every application, just like it makes sense to have only
one instance of WindowManager or FileSystem.
A class which allows only one instance of itself
is called Singleton and is well known creational Design Pattern.
How do we implement Singleton in Java?
First of all we need to make sure that user
can only create one instance of this class. We start by making constructor
private and introducing static method called getInstance like this
Produces something like this:
As you can see it is pretty easy to implement Singleton in Java.
Now, the Log should be a singleton, there should
be only one instance of the log object in your application.
One thing that we note is that we dont want to be saying
Produces something like this:
We want to say instead:
Produces something like this:
How do we do this?
Well, our Log will not be exactly a singleton - it will not allow
any instances of itself at all. All of its methods will be static and it will not
have getInstance method. This type of class is called a utility class.
There are two error methods: one, which takes just an error message, and another,
which takes an object as well. The second one is there so that we can print the name of the class,
which is logging the message:
What kinds of levels for logging would you want?
Probably, it is sufficient to have debug, warning and error.
One more important thing that we need to address is that
sometimes we want the output of the log to go to a file,
sometimes to the standard output and sometimes to the Text Area
in the GUI. It would be nice to have this functionality within our Log.
A simple way to do this is to create an interface ILogImpl, and
make it pluggable into Log.
Our Log will delegate the actual printing to the ILogImpl, so
we will have:
First, this technique of delegating the implementation
of the actual operation (or creating an interface for the implementation)
is essentially another Design Pattern called Bridge. Second, by outsourcing
the printing we now can plug-in different ILogImpl implementations.
For example to print to standard output we would do this:
The complete Log looks like this now:
Please read
Arnold & Gosling Java Programming language
Chapters on Exceptions and Garbage Collection
Chapters 16 and 25 from Agile Software Development Book
Assignment 1
Define & implement Log, ILogImpl, StdOutLogImpl, FileLogImpl and MultiLogImpl
|