This section suggests a standard organization for interface documentation (see Figure 7.1). Like all templates and organizational layouts in this book, you may wish to modify this one to remove items not relevant to your situation or to add items unique to your business. More important than which standard organization you use is the practice of using one. Use what you need to present an accurate picture of the element's externally visible interactions for the interfaces in your project.
Guidelines for Writing Down the Semantics of a Resource
In addition, the statement of semantics should make it clear whether the execution of the resource will be atomic or may be suspended or interrupted.
Consider using preconditions and postconditions for documenting both resource usage restrictions and resource semantics together. A precondition states what must be true before the interaction is permitted; a postcondition describes any state changes resulting from the interaction.
Notions of persistence or side effects can be relevant here. If the resource requires other resources to be present or makes certain other assumptions about its environment, these should be documented. Some restrictions are less prohibitive; for example, Java interfaces can list certain methods as deprecated, meaning that users should not use them, as they will likely be unsupported in future versions of the interface. Usage restrictions are often documented by defining exceptions that will be raised if the restrictions are violated.
Figure 7.1. Documentation for an interface consists of nine parts
COMING TO TERMS
Exceptions and Error Handling
When designing an interface, architects naturally concentrate on documenting how resources work in the nominal case, when everything goes according to plan. The real world, of course, is far from nominal, and a well-designed system must take appropriate action in the face of undesired circumstances. What happens when a resource is called with parameters that make no sense? What happens when the resource requires more memory, but the allocation request fails because there isn't any more? What happens when a resource never returns, because it has fallen victim to a process deadlock? What happens when the software is supposed to read the value of a sensor, but the sensor has failed and either isn't responding or is responding with gibberish?
Terminating the program on the spot seldom qualifies as "appropriate action." More desirable alternatives, depending on the situation, include various combinations of the following:
These are all reasonable actions that a resource can take in the presence of undesired circumstances. If a resource is designed to take any of these actions, that should simply be documented as part of the effects of that resource. But many times, something else is appropriate. The resource can, in effect, throw up its hands and report that an error condition existed and that it was unable to do its job. This is where old-fashioned programs would print an error message and terminate. Today, they often raise an exception, which allows execution to continue and perhaps accomplish useful work.
Making a strong distinction between detecting an error condition and handling it provides greater flexibility in taking corrective action. The right place to fix a problem raised by a resource is usually the actor that invoked it, not in the resource itself. The resource detects the problem; the actor handles it. If we're in development, handling it might mean terminating with an error message so the bug can be tracked down and fixed. Perhaps the actor made the mistake because one of its own resources was used incorrectly by another actor. In that case, the actor might handle the exception by raising an exception of its own and bubbling the responsibility back along the invocation chain until the actor ultimately responsible is notified.
Modern programming languages provide facilities for raising exceptions and assigning handlers. Program language reference manuals take a language-oriented view in classifying the world of exceptions. The C++ programming language, for instance, has built-in exceptions classes dealing with memory allocation failure, process failure, tasking failures, and the like. Those are exceptions that the compiled program is likely to encounter from the operating system.
But many other things can go wrong during execution of software, and it is incumbent on the architect to say what they are. An architecture-oriented classification of exceptions is summarized in Figure 7.2. In the context of an element's interface, exception conditions are one of the following:
Figure 7.2. A classification of exceptions associated with a resource on an element's interface
Exceptions and effects produce a three-way division of the state space for every resource on an interface.
In a perfect world, the architect squeezes the failure set to nothingness by moving failure states to the competence set by expanding the statement of effects, or to the exception set by creating more exceptions. An equally valid approach is to make a convincing argument that the program cannot possibly get into a state in the failure set.
For example, suppose that element E needs to have complete control of a shared device during the execution of resource R on interface I. If the architect wasn't sure that this would always be the case when R was invoked, he or she would either (1) specify what the element would do if the device was already in usereturn immediately with a failure code, retry a set number of times, wait a set periodor (2) define an exception for R that reported the condition back to the actor and made it the actor's responsibility to sort out. But perhaps the architect is certain that the device will never be in use, because element E is the only element that uses it. So the architect doesn't define behavior for the resource to account for that condition and doesn't define an exception for it, either. This puts the condition in the resource's failure set, but the architect can make a convincing argument that doing so is safe.
For each resource in an interface, do the following:
Software Architectures and Documentation
Part I. Software Architecture Viewtypes and Styles
The Module Viewtype
Styles of the Module Viewtype
The Component-and-Connector Viewtype
Styles of the Component-and-Connector Viewtype
The Allocation Viewtype and Styles
Part II. Software Architecture Documentation in Practice
Documenting Software Interfaces
Choosing the Views
Building the Documentation Package
Other Views and Beyond
Rationale, Background, and Design Constraints