Software Design Case Study - Generic Error Object
Often it is not enough to know that an error occurred. A program may need to know specifically which error occurred. If we only allow one error at a time, then a single variable would suffice. However, in most applications several errors can occur, and not all are fatal. At that point, most C programmers use an unsigned int or a DWORD to hold error bits, allowing up to 32 individual error flags to be set and cleared. But what happens when another error is added?
The intent of this object is to create an error object that still behaves as a bit-field, but allows more than 32 bits of error flags. It was also desirable to be able to set, check, or clear multiple errors simultaneously. This object encapsulates the error flags and error checking into an object, so that the programmer only has to deal with a single error object.
The interface was designed by determining what information the object would need to hold, and how the application would interact with it. I also decided that one size would not fit everyone: this object had to provide the type of error handling that was appropriate for our range of applications - not be the Mother of all Error Objects, the last error object you would ever have to buy. Therefore, I decided to stick with a somewhat C-like approach and use error bits instead of enums, as that was best suited to our needs.
Many errors that the applications would generate would not fatal, and would only affect downstream processing. That meant that the object had to be able to ignore some errors. Also, one user action had the potential of generating many errors internally; however, I did not want the user to have to respond to a cascading series of error messages. I wanted to queue the errors for display at a more appropriate point in the processing. I also decided that the error object had to be entirely unaware of the user interface; I decided that error notifications would be sent to the user interface via SendMessage(). That way, the error object could remain oblivious to the rest of the application or the user interface.
Another limitation was the fact that, although the object should be responsible for returning a text description of the error, that information would be application specific. Since the source code had to be portable, that meant that the interface had to allow another object to initially load the text strings. This meant that the error object could not entirely stand alone. In practice, I decided to use the generic error object class as a base class, and have a sub-class define the error strings, and have the constructor load them from a resource file. This same derived class would also define the application specific error flags.
After analysis, I decided that the object had to:
The basic methods needed are:
Iterator functions were added to allow the client program to scan the object for errors. This would be useful when displaying all errors to the user.
Lastly, string functions were needed to get a descriptive string for the error.
The implementation was done in an MFC application, but there is no MFC code used in the module. If not used in an MFC application, delete the include for stdafx.h and include the tchar.h file.
Click here to get the C++ source code. This is the same source code as for case study #5.
The implementation was done by creating a dialog-based C++ project. This project would act as the object's test shell. The CErrorObject files had to be separate, so that they could be ported from the test shell to the final application. This allowed the error object to be coded to the interface, tested, and also ensured portability of the source code. It also allowed the code to be written and tested in parallel with development of the main application.
Unit tests were written to the interface specification. The test shell's UI allowed errors to be set, cleared, and displayed. This allowed the code to be fully debugged before integrating it into the main application.
A stub CErrorObject was created simply to allow main program development to continue. This shell contained the interface, but no functionality. Once the error object code was completed, it could be added to the application and the error handling would appear!