Contracts for components
A variant of this article appeared in Software Development Magazine, July 2000, as part of the "Beyond Objects" column alternatively written by Clemens Szyperski, Grady Booch and Bertrand Meyer (and originally Bruce Powel Douglass). ©Software Development Magazine, 2000.
Interface Definition Languages as we know them today are doomed.
It's sometimes only after a technology has established itself that one can start to understand what it really is about. In the case of component-based development, I hope that we can do better. The three co-columnists (Bruce Powell Douglas, Clemens Szyperski and I) are trying, each with our own separate voice, background and interests, to analyze the technology as it happens. Not just analyze but influence too: since we are all active participants in the field, rather than dispassionate observers, we all have a vested interest to make sure that it happens right and fulfills its promises. As I have hinted in earlier columns, one of the incontrovertible conditions is that we properly tackle the issue of specifying components. The word 'contract' has already been tossed out a few times in this respect (by myself as well as Clemens and Bruce), and it's time to see more closely what it means for components.
Component-based development is still very new, but we are starting to have enough perspective to see what it's about. Components as we have them today result, I think, from the confluence of two major developments of the nineties:
(I must also mention a third phenomenon, without which none of this would have happened: the general trend towards standardization of interfaces, most visibly through the universal deployment of the Windows API, Linux's potential notwithstanding. As this is the background for much of the extraordinary recent development of the computer industry, we don't need to delve on it further.)
The major component standards - COM, CORBA, EJB - attempt to capitalize on the power of object-oriented principles to generalize 'controls' to full-fledged software components, similar in spirit to the tangible components of other engineering disciplines (electronics, machinery...), and applicable to any software need, not just user interfaces. What is still missing in this effort, however, is a standard way to specify the components.
The pipe and the painting
In his book on Component Software, Clemens Szyperski rightly points out that software components differ in some intrinsic ways from engineering components in (for example) electronics. These differences are natural since the analogy with electronics is only a metaphor, and not all properties from one field apply to the other. A metaphor is not an identity. But one property of components applies to any field of engineering: we cannot seriously have component-based development without developing, for every component, a specification of the component, distinct from the component itself, and serving as the sole basis for users ('clients') of the component.
Why is this rule, so obvious in all other fields, not universally applied in software? The answer, I think, lies in the specific nature of software and the fuzziness that it creates between description and described. The just stated requirement that the specification should be "distinct from the component itself" is a platitude in electronics or construction engineering: no one would confuse a circuit with a circuit diagram, or a bridge with a map of the bridge. You can fall from the bridge, but not from the map. You won't usually get an electric shock from the circuit diagram. Remember the famous Magritte painting of a pipe, entitled "Ceci n'est pas une pipe" (this is not a pipe.) But with software the platitude turns into a tricky question, because separating specification from implementation is not that straightforward any more. Otherwise we wouldn't be screaming at each other, during design discussions, things like "But this is only an implementation matter!". And information hiding wouldn't be such a key issue.
I cited information hiding, in my first column, as the key determinant of the appeal of binary component standards, noting that binary components make information hiding inevitable. But information hiding, and components in any decent form, can only work with a specification. You can only trust components to the extent that you know what they do. This is where current approaches don't quite deliver yet.
Interface Definition Languages and their limitations
Both the CORBA and COM standards use, as one of their central concepts, an Interface Definition Language. It's a little confusing because they use the same name and acronym, IDL, for two distinct languages. But this shouldn't bother us here because the two IDLs are in fact quite close in their concepts. If you don't know IDL (in either variant), you in fact almost know it anyway if you have done any C++, since an IDL class specification is very similar to a C++ header file - the part that gives the signature of the operations, without their implementations. Here for example is a CORBA IDL specification of a function from a component in charge of making airplane reservations:
This says that the component offers an operation ReserveFlight that expects as argument a list of seats to be reserved, returns a boolean value (presumably an indication of success - personally I would never use such side-effect-producing functions, but this example illustrates the prevalent programming style), and may raise certain exceptions in case things don't go as smoothly as we would like.
Studying current IDLs leads to two immediate remarks. The first is that IDLs as we know them today are doomed. Not the notion of interface, but the idea that you should write an interface as a separate effort. For a few stable components it may work; but in the long term this is a broken approach for at least two reasons:
This is why the standard CORBA approach of writing an IDL spec and using an IDL compiler to generate a module template in a programming language seems to go the wrong way around. You should concentrate on writing the software, and let tools produce its interfaces. This rule is not affected by the observation that, in most cases, you can't extract a class from a normal O-O system and make it into a component, but need instead to write a few interface classes to serve as your application's component façade to the rest of the world: even so, you will want to write your façade classes in your programming language of choice, not yet another formalism.
A number of CORBA tools are now available to do just that - decompilation into IDL. In the COM world, ISE’s EiffelCOM wizard (http://eiffel.com/products/com) enables you interactively to wrap Eiffel applications into COM components, and other languages’ COM components into Eiffel façades for use from Eiffel. It seems clear anyway that the evolution of COM technologies, as will be described in my next column, will move away from IDL as something that must be coded separately.
What is a spec?
Separateness is not the only problem of the current IDLs. Their most striking limitation has been mentioned in earlier columns: they stay within the limits of purely structural specifications. The above specification of ReserveFlight says very little in terms of actual semantic specifications. It gives the name of the operation all rights, the number and types of its arguments, and the list of abnormal cases it is prepared to encounter and signal, but nothing about what it actually does.
Yet this is the kind of thing that we would expect from a specification in the full sense of the term. An electrical component's spec will tell us about global operating conditions (temperature and so on), acceptable input signals (voltage and amperage limits), output signals as a function of the input, all this independently of the specific implementation technology. We need the same three kinds of specification elements for a software component:
These concepts of Design by Contract have been extensively applied to the development of object-oriented software in Eiffel, where they are part of the basic fabric of the language, and are increasingly being applied to other approaches such as Java (through tools like iContract), C++ (through macro packages like Nana), UML (through the Object Constraint Language). For links to references on these tools and many others, see the designbycontract.com portal described in the sidebar.The concepts are even more necessary in connection with component standards such as CORBA, COM and EJB since, as I pointed out in an earlier column, "it's information hiding, stupid": with binary components we don't have the last resort of going to the source code. Yet a specification such as the above is badly lacking in this respect. The closest it comes to specifying the semantics is by listing the exceptions. But this leads to a programming style heavily based on exception handling, which is not healthy; and of course it is rather paradoxical that we should specify to our clients what abnormal conditions can arise, but not take the trouble to tell them what the normal expected behavior is!
A number of proposals have been made to add contracts to CORBA and COM; again, look at designbycontract.com for pointers. We are currently building a language-independent contract framework for COM, which I will describe in a later column.
As Clemens Szyperski hinted in one of his last columns, transposing the notion of contract from the world of classical programming to the world of component-based development raises a few challenges. We can point out the following problems:
On the last point, my colleagues Christine Mingins and Jean-Marc Jézéquel (whom I plan to invite to contribute a guest column later in this series) distinguish four levels of contracting: basic type contracts, such as exist in typed programming languages as well as the current IDLs; semantic contracts, such as the ones cited above, expressing invariants, preconditions and postconditions; performance contracts, so critical for example in real-time applications; and quality of service contracts, harder to quantify but just as important in practice, as anyone who has tried to access his E*Trade account at the time of highest market volatility (such as the time of writing this column) will have noted.
The general idea of the Trusted Components effort (http://www.trusted-components.org) is to explore these issues to find out if we can build a repertoire of high-quality components — components to which we can really entrust the reliability of our applications.
The difficulty of these issues explains why I haven't yet included in the present column a description of my proposed CDL (Contract Definition Language) as announced in the previous installment. I realized that this was still premature; we need first to explore the background in some depth. Later columns will continue in this direction, and will at due point include the CDL proposal.
The three questions of software contracting
In the meantime, it is essential to note that no good component can exist without a good specification. Even if you do not have access to languages and tools officially supporting the Design by Contract methodology, the time to start using the principles is now. The good news is that it's not difficult. We are not talking about fully formal approaches, such as Jean-Raymond Abrial's B method and tools, described in his B Book (Cambridge University Press, 1996 - surgeon general's warning: not your typical Stephen King, and not for the mathematically faint of heart), and used recently to prove, mathematically, the correctness of the security aspects of the latest, driver-less, line of the Paris metro, a 100,000-line program. For most of us, this is still beyond our reach. We are talking about something much simpler - the three questions.
In Design by Contract we keep asking three questions:
You will of course have recognized in these questions the notions of class invariant, operation precondition, and operation postcondition. Don't ever write any component, in any sense of the term (my and Bruce’s broader sense, or Clemens's narrower one), without asking the Three Questions of Software Contracting. Component-based development, and just good software development, comes at that small price.